mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 16:48:16 +08:00
refa: live page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -737,10 +737,6 @@ class Api {
|
||||
/// 稍后再看&收藏夹视频列表
|
||||
static const String mediaList = '/x/v2/medialist/resource/list';
|
||||
|
||||
/// 我的关注 - 正在直播
|
||||
static const String getFollowingLive =
|
||||
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/following';
|
||||
|
||||
static const String pgcIndexCondition = '/pgc/season/index/condition';
|
||||
|
||||
static const String pgcIndexResult = '/pgc/season/index/result';
|
||||
@@ -800,4 +796,10 @@ class Api {
|
||||
static const String voteInfo = '/x/vote/vote_info';
|
||||
|
||||
static const String doVote = '/x/vote/do_vote';
|
||||
|
||||
static const String liveFeedIndex =
|
||||
'${HttpString.liveBaseUrl}/xlive/app-interface/v2/index/feed';
|
||||
|
||||
static const String liveFollow =
|
||||
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/following';
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/live/danmu_info.dart';
|
||||
import 'package:PiliPlus/models/live/follow.dart';
|
||||
import 'package:PiliPlus/models/live/item.dart';
|
||||
import 'package:PiliPlus/models/live/live_emoticons/data.dart';
|
||||
import 'package:PiliPlus/models/live/live_emoticons/datum.dart';
|
||||
import 'package:PiliPlus/models/live/room_info.dart';
|
||||
import 'package:PiliPlus/models/live/room_info_h5.dart';
|
||||
import 'package:PiliPlus/models/live/live_feed_index/data.dart';
|
||||
import 'package:PiliPlus/models/live/live_follow/data.dart';
|
||||
import 'package:PiliPlus/models/live/live_room/danmu_info.dart';
|
||||
import 'package:PiliPlus/models/live/live_room/item.dart';
|
||||
import 'package:PiliPlus/models/live/live_room/room_info.dart';
|
||||
import 'package:PiliPlus/models/live/live_room/room_info_h5.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:PiliPlus/utils/wbi_sign.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
@@ -150,31 +152,6 @@ class LiveHttp {
|
||||
}
|
||||
}
|
||||
|
||||
// 我的关注 正在直播
|
||||
static Future liveFollowing({required int pn, required int ps}) async {
|
||||
var res = await Request().get(
|
||||
Api.getFollowingLive,
|
||||
queryParameters: {
|
||||
'page': pn,
|
||||
'page_size': ps,
|
||||
'platform': 'web',
|
||||
'ignoreRecord': 1,
|
||||
'hit_ab': true,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': LiveFollowingModel.fromJson(res.data['data'])
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
'msg': res.data['message'],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState<List<LiveEmoteDatum>?>> getLiveEmoticons(
|
||||
{required int roomId}) async {
|
||||
var res = await Request().get(
|
||||
@@ -191,4 +168,65 @@ class LiveHttp {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState<LiveIndexData>> liveFeedIndex({
|
||||
required int pn,
|
||||
required bool isLogin,
|
||||
}) async {
|
||||
final params = {
|
||||
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'actionKey': 'appkey',
|
||||
'build': '8350200',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'device_name': 'vivo',
|
||||
'device_type': '0',
|
||||
'fnval': '912',
|
||||
'disable_rcmd': '0',
|
||||
'https_url_req': '1',
|
||||
'login_event': isLogin ? '1' : '0',
|
||||
'mobi_app': 'android_hd',
|
||||
'module_select': '0',
|
||||
'network': 'wifi',
|
||||
'page': pn.toString(),
|
||||
'platform': 'android',
|
||||
if (isLogin) 'relation_page': '1',
|
||||
's_locale': 'zh_CN',
|
||||
'scale': '2',
|
||||
'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
};
|
||||
Utils.appSign(
|
||||
params,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
var res = await Request().get(
|
||||
Api.liveFeedIndex,
|
||||
queryParameters: params,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(LiveIndexData.fromJson(res.data['data']));
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState<LiveFollowData>> liveFollow(int page) async {
|
||||
var res = await Request().get(
|
||||
Api.liveFollow,
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'page_size': 9,
|
||||
'ignoreRecord': 1,
|
||||
'hit_ab': true,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(LiveFollowData.fromJson(res.data['data']));
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
class LiveFollowingModel {
|
||||
int? count;
|
||||
List<LiveFollowingItemModel>? list;
|
||||
int? liveCount;
|
||||
int? neverLivedCount;
|
||||
List? neverLivedFaces;
|
||||
int? pageSize;
|
||||
String? title;
|
||||
int? totalPage;
|
||||
|
||||
LiveFollowingModel({
|
||||
this.count,
|
||||
this.list,
|
||||
this.liveCount,
|
||||
this.neverLivedCount,
|
||||
this.neverLivedFaces,
|
||||
this.pageSize,
|
||||
this.title,
|
||||
this.totalPage,
|
||||
});
|
||||
|
||||
LiveFollowingModel.fromJson(Map<String, dynamic> json) {
|
||||
count = json['count'];
|
||||
list = (json['list'] as List?)
|
||||
?.map((item) => LiveFollowingItemModel.fromJson(item))
|
||||
.toList();
|
||||
liveCount = json['live_count'];
|
||||
neverLivedCount = json['never_lived_count'];
|
||||
neverLivedFaces = json['never_lived_faces'];
|
||||
pageSize = json['pageSize'];
|
||||
title = json['title'];
|
||||
totalPage = json['totalPage'];
|
||||
}
|
||||
}
|
||||
|
||||
class LiveFollowingItemModel {
|
||||
int? roomId;
|
||||
int? uid;
|
||||
String? uname;
|
||||
String? title;
|
||||
String? face;
|
||||
int? liveStatus;
|
||||
int? recordNum;
|
||||
String? recentRecordId;
|
||||
int? isAttention;
|
||||
int? clipNum;
|
||||
int? fansNum;
|
||||
String? areaName;
|
||||
String? areaValue;
|
||||
String? tags;
|
||||
String? recentRecordIdV2;
|
||||
int? recordNumV2;
|
||||
int? recordLiveTime;
|
||||
String? areaNameV2;
|
||||
String? roomNews;
|
||||
String? watchIcon;
|
||||
String? textSmall;
|
||||
String? roomCover;
|
||||
String? pic;
|
||||
int? parentAreaId;
|
||||
int? areaId;
|
||||
|
||||
LiveFollowingItemModel({
|
||||
this.roomId,
|
||||
this.uid,
|
||||
this.uname,
|
||||
this.title,
|
||||
this.face,
|
||||
this.liveStatus,
|
||||
this.recordNum,
|
||||
this.recentRecordId,
|
||||
this.isAttention,
|
||||
this.clipNum,
|
||||
this.fansNum,
|
||||
this.areaName,
|
||||
this.areaValue,
|
||||
this.tags,
|
||||
this.recentRecordIdV2,
|
||||
this.recordNumV2,
|
||||
this.recordLiveTime,
|
||||
this.areaNameV2,
|
||||
this.roomNews,
|
||||
this.watchIcon,
|
||||
this.textSmall,
|
||||
this.roomCover,
|
||||
this.pic,
|
||||
this.parentAreaId,
|
||||
this.areaId,
|
||||
});
|
||||
|
||||
LiveFollowingItemModel.fromJson(Map<String, dynamic> json) {
|
||||
roomId = json['roomid'];
|
||||
uid = json['uid'];
|
||||
uname = json['uname'];
|
||||
title = json['title'];
|
||||
face = json['face'];
|
||||
liveStatus = json['live_status'];
|
||||
recordNum = json['record_num'];
|
||||
recentRecordId = json['recent_record_id'];
|
||||
isAttention = json['is_attention'];
|
||||
clipNum = json['clipnum'];
|
||||
fansNum = json['fans_num'];
|
||||
areaName = json['area_name'];
|
||||
areaValue = json['area_value'];
|
||||
tags = json['tags'];
|
||||
recentRecordIdV2 = json['recent_record_id_v2'];
|
||||
recordNumV2 = json['record_num_v2'];
|
||||
recordLiveTime = json['record_live_time'];
|
||||
areaNameV2 = json['area_name_v2'];
|
||||
roomNews = json['room_news'];
|
||||
watchIcon = json['watch_icon'];
|
||||
textSmall = json['text_small'];
|
||||
roomCover = json['room_cover'];
|
||||
pic = json['room_cover'];
|
||||
parentAreaId = json['parent_area_id'];
|
||||
areaId = json['area_id'];
|
||||
}
|
||||
}
|
||||
25
lib/models/live/live_feed_index/avatar.dart
Normal file
25
lib/models/live/live_feed_index/avatar.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
class Avatar {
|
||||
String? cover;
|
||||
String? event;
|
||||
String? text;
|
||||
int? uid;
|
||||
String? url;
|
||||
|
||||
Avatar({this.cover, this.event, this.text, this.uid, this.url});
|
||||
|
||||
factory Avatar.fromJson(Map<String, dynamic> json) => Avatar(
|
||||
cover: json['cover'] as String?,
|
||||
event: json['event'] as String?,
|
||||
text: json['text'] as String?,
|
||||
uid: json['uid'] as int?,
|
||||
url: json['url'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'cover': cover,
|
||||
'event': event,
|
||||
'text': text,
|
||||
'uid': uid,
|
||||
'url': url,
|
||||
};
|
||||
}
|
||||
20
lib/models/live/live_feed_index/calendar_button.dart
Normal file
20
lib/models/live/live_feed_index/calendar_button.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
class CalendarButton {
|
||||
String? text;
|
||||
String? link;
|
||||
|
||||
CalendarButton({this.text, this.link});
|
||||
|
||||
factory CalendarButton.fromJson(Map<String, dynamic> json) {
|
||||
return CalendarButton(
|
||||
text: json['text'] as String?,
|
||||
link: json['link'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'text': text,
|
||||
'link': link,
|
||||
};
|
||||
}
|
||||
32
lib/models/live/live_feed_index/card_data.dart
Normal file
32
lib/models/live/live_feed_index/card_data.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart';
|
||||
|
||||
import 'card_data_item.dart';
|
||||
|
||||
class CardData {
|
||||
CardDataItem? bannerV2;
|
||||
CardDataItem? myIdolV1;
|
||||
CardLiveItem? smallCardV1;
|
||||
|
||||
CardData({
|
||||
this.bannerV2,
|
||||
this.myIdolV1,
|
||||
this.smallCardV1,
|
||||
});
|
||||
|
||||
factory CardData.fromJson(Map<String, dynamic> json) => CardData(
|
||||
bannerV2: json['banner_v2'] == null
|
||||
? null
|
||||
: CardDataItem.fromJson(json['banner_v2'] as Map<String, dynamic>),
|
||||
myIdolV1: json['my_idol_v1'] == null
|
||||
? null
|
||||
: CardDataItem.fromJson(json['my_idol_v1'] as Map<String, dynamic>),
|
||||
smallCardV1: json['small_card_v1'] == null
|
||||
? null
|
||||
: CardLiveItem.fromJson(
|
||||
json['small_card_v1'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'banner_v2': bannerV2?.toJson(),
|
||||
};
|
||||
}
|
||||
44
lib/models/live/live_feed_index/card_data_item.dart
Normal file
44
lib/models/live/live_feed_index/card_data_item.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart';
|
||||
|
||||
import 'module_info.dart';
|
||||
|
||||
class CardDataItem {
|
||||
ModuleInfo? moduleInfo;
|
||||
List<CardLiveItem>? list;
|
||||
dynamic topView;
|
||||
ExtraInfo? extraInfo;
|
||||
|
||||
CardDataItem({
|
||||
this.moduleInfo,
|
||||
this.list,
|
||||
this.topView,
|
||||
this.extraInfo,
|
||||
});
|
||||
|
||||
factory CardDataItem.fromJson(Map<String, dynamic> json) => CardDataItem(
|
||||
moduleInfo: json['module_info'] == null
|
||||
? null
|
||||
: ModuleInfo.fromJson(json['module_info'] as Map<String, dynamic>),
|
||||
list: (json['list'] as List<dynamic>?)
|
||||
?.map((e) => CardLiveItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
topView: json['top_view'] as dynamic,
|
||||
extraInfo: json['extra_info'] == null
|
||||
? null
|
||||
: ExtraInfo.fromJson(json['extra_info'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'module_info': moduleInfo?.toJson(),
|
||||
'list': list?.map((e) => e.toJson()).toList(),
|
||||
'top_view': topView,
|
||||
};
|
||||
}
|
||||
|
||||
class ExtraInfo {
|
||||
int? totalCount;
|
||||
|
||||
ExtraInfo.fromJson(Map<String, dynamic> json) {
|
||||
totalCount = json['total_count'];
|
||||
}
|
||||
}
|
||||
187
lib/models/live/live_feed_index/card_data_list_item.dart
Normal file
187
lib/models/live/live_feed_index/card_data_list_item.dart
Normal file
@@ -0,0 +1,187 @@
|
||||
import 'quality_description.dart';
|
||||
import 'watched_show.dart';
|
||||
|
||||
class CardLiveItem {
|
||||
int? id;
|
||||
int? roomid;
|
||||
int? uid;
|
||||
String? uname;
|
||||
String? face;
|
||||
String? cover;
|
||||
String? title;
|
||||
int? area;
|
||||
int? liveTime;
|
||||
String? areaName;
|
||||
int? areaV2Id;
|
||||
String? areaV2Name;
|
||||
String? areaV2ParentName;
|
||||
int? areaV2ParentId;
|
||||
String? liveTagName;
|
||||
int? online;
|
||||
String? playUrl;
|
||||
String? playUrlH265;
|
||||
List? acceptQuality;
|
||||
int? currentQuality;
|
||||
int? pkId;
|
||||
String? link;
|
||||
int? specialAttention;
|
||||
int? broadcastType;
|
||||
String? pendentRu;
|
||||
String? pendentRuColor;
|
||||
String? pendentRuPic;
|
||||
int? officialVerify;
|
||||
int? currentQn;
|
||||
List<QualityDescription>? qualityDescription;
|
||||
String? playUrlCard;
|
||||
int? flag;
|
||||
List<dynamic>? pendentList;
|
||||
int? p2pType;
|
||||
String? sessionId;
|
||||
int? groupId;
|
||||
WatchedShow? watchedShow;
|
||||
int? isNft;
|
||||
String? nftDmark;
|
||||
String? statusText;
|
||||
String? darkFace;
|
||||
String? trackid;
|
||||
|
||||
CardLiveItem({
|
||||
this.id,
|
||||
this.roomid,
|
||||
this.uid,
|
||||
this.uname,
|
||||
this.face,
|
||||
this.cover,
|
||||
this.title,
|
||||
this.area,
|
||||
this.liveTime,
|
||||
this.areaName,
|
||||
this.areaV2Id,
|
||||
this.areaV2Name,
|
||||
this.areaV2ParentName,
|
||||
this.areaV2ParentId,
|
||||
this.liveTagName,
|
||||
this.online,
|
||||
this.playUrl,
|
||||
this.playUrlH265,
|
||||
this.acceptQuality,
|
||||
this.currentQuality,
|
||||
this.pkId,
|
||||
this.link,
|
||||
this.specialAttention,
|
||||
this.broadcastType,
|
||||
this.pendentRu,
|
||||
this.pendentRuColor,
|
||||
this.pendentRuPic,
|
||||
this.officialVerify,
|
||||
this.currentQn,
|
||||
this.qualityDescription,
|
||||
this.playUrlCard,
|
||||
this.flag,
|
||||
this.pendentList,
|
||||
this.p2pType,
|
||||
this.sessionId,
|
||||
this.groupId,
|
||||
this.watchedShow,
|
||||
this.isNft,
|
||||
this.nftDmark,
|
||||
this.statusText,
|
||||
this.darkFace,
|
||||
this.trackid,
|
||||
});
|
||||
|
||||
factory CardLiveItem.fromJson(Map<String, dynamic> json) => CardLiveItem(
|
||||
id: json['id'] as int?,
|
||||
roomid: json['roomid'] as int?,
|
||||
uid: json['uid'] as int?,
|
||||
uname: json['uname'] as String?,
|
||||
face: json['face'] as String?,
|
||||
cover: json['cover'] as String?,
|
||||
title: json['title'] as String?,
|
||||
area: json['area'] as int?,
|
||||
liveTime: json['live_time'] as int?,
|
||||
areaName: json['area_name'] as String?,
|
||||
areaV2Id: json['area_v2_id'] as int?,
|
||||
areaV2Name: json['area_v2_name'] as String?,
|
||||
areaV2ParentName: json['area_v2_parent_name'] as String?,
|
||||
areaV2ParentId: json['area_v2_parent_id'] as int?,
|
||||
liveTagName: json['live_tag_name'] as String?,
|
||||
online: json['online'] as int?,
|
||||
playUrl: json['play_url'] as String?,
|
||||
playUrlH265: json['play_url_h265'] as String?,
|
||||
acceptQuality: json['accept_quality'],
|
||||
currentQuality: json['current_quality'] as int?,
|
||||
pkId: json['pk_id'] as int?,
|
||||
link: json['link'] as String?,
|
||||
specialAttention: json['special_attention'] as int?,
|
||||
broadcastType: json['broadcast_type'] as int?,
|
||||
pendentRu: json['pendent_ru'] as String?,
|
||||
pendentRuColor: json['pendent_ru_color'] as String?,
|
||||
pendentRuPic: json['pendent_ru_pic'] as String?,
|
||||
officialVerify: json['official_verify'] as int?,
|
||||
currentQn: json['current_qn'] as int?,
|
||||
qualityDescription: (json['quality_description'] as List<dynamic>?)
|
||||
?.map((e) => QualityDescription.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
playUrlCard: json['play_url_card'] as String?,
|
||||
flag: json['flag'] as int?,
|
||||
pendentList: json['pendent_list'] as List<dynamic>?,
|
||||
p2pType: json['p2p_type'] as int?,
|
||||
sessionId: json['session_id'] as String?,
|
||||
groupId: json['group_id'] as int?,
|
||||
watchedShow: json['watched_show'] == null
|
||||
? null
|
||||
: WatchedShow.fromJson(
|
||||
json['watched_show'] as Map<String, dynamic>),
|
||||
isNft: json['is_nft'] as int?,
|
||||
nftDmark: json['nft_dmark'] as String?,
|
||||
statusText: json['status_text'] as String?,
|
||||
darkFace: json['dark_face'] as String?,
|
||||
trackid: json['trackid'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'roomid': roomid,
|
||||
'uid': uid,
|
||||
'uname': uname,
|
||||
'face': face,
|
||||
'cover': cover,
|
||||
'title': title,
|
||||
'area': area,
|
||||
'live_time': liveTime,
|
||||
'area_name': areaName,
|
||||
'area_v2_id': areaV2Id,
|
||||
'area_v2_name': areaV2Name,
|
||||
'area_v2_parent_name': areaV2ParentName,
|
||||
'area_v2_parent_id': areaV2ParentId,
|
||||
'live_tag_name': liveTagName,
|
||||
'online': online,
|
||||
'play_url': playUrl,
|
||||
'play_url_h265': playUrlH265,
|
||||
'accept_quality': acceptQuality,
|
||||
'current_quality': currentQuality,
|
||||
'pk_id': pkId,
|
||||
'link': link,
|
||||
'special_attention': specialAttention,
|
||||
'broadcast_type': broadcastType,
|
||||
'pendent_ru': pendentRu,
|
||||
'pendent_ru_color': pendentRuColor,
|
||||
'pendent_ru_pic': pendentRuPic,
|
||||
'official_verify': officialVerify,
|
||||
'current_qn': currentQn,
|
||||
'quality_description':
|
||||
qualityDescription?.map((e) => e.toJson()).toList(),
|
||||
'play_url_card': playUrlCard,
|
||||
'flag': flag,
|
||||
'pendent_list': pendentList,
|
||||
'p2p_type': p2pType,
|
||||
'session_id': sessionId,
|
||||
'group_id': groupId,
|
||||
'watched_show': watchedShow?.toJson(),
|
||||
'is_nft': isNft,
|
||||
'nft_dmark': nftDmark,
|
||||
'status_text': statusText,
|
||||
'dark_face': darkFace,
|
||||
'trackid': trackid,
|
||||
};
|
||||
}
|
||||
20
lib/models/live/live_feed_index/card_list.dart
Normal file
20
lib/models/live/live_feed_index/card_list.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'card_data.dart';
|
||||
|
||||
class LiveCardList {
|
||||
String? cardType;
|
||||
CardData? cardData;
|
||||
|
||||
LiveCardList({this.cardType, this.cardData});
|
||||
|
||||
factory LiveCardList.fromJson(Map<String, dynamic> json) => LiveCardList(
|
||||
cardType: json['card_type'] as String?,
|
||||
cardData: json['card_data'] == null
|
||||
? null
|
||||
: CardData.fromJson(json['card_data'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'card_type': cardType,
|
||||
'card_data': cardData?.toJson(),
|
||||
};
|
||||
}
|
||||
44
lib/models/live/live_feed_index/data.dart
Normal file
44
lib/models/live/live_feed_index/data.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
import 'card_list.dart';
|
||||
|
||||
class LiveIndexData {
|
||||
List<LiveCardList>? cardList;
|
||||
int? isRollback;
|
||||
int? hasMore;
|
||||
int? triggerTime;
|
||||
int? isNeedRefresh;
|
||||
|
||||
LiveIndexData({
|
||||
this.cardList,
|
||||
this.isRollback,
|
||||
this.hasMore,
|
||||
this.triggerTime,
|
||||
this.isNeedRefresh,
|
||||
});
|
||||
|
||||
LiveIndexData.fromJson(Map<String, dynamic> json) {
|
||||
if ((json['card_list'] as List<dynamic>?)?.isNotEmpty == true) {
|
||||
cardList = <LiveCardList>[];
|
||||
// banner_v2
|
||||
// my_idol_v1
|
||||
// area_entrance_v3
|
||||
// small_card_v1
|
||||
for (var json in json['card_list']) {
|
||||
if (const ['my_idol_v1', 'small_card_v1'].contains(json['card_type'])) {
|
||||
cardList!.add(LiveCardList.fromJson(json));
|
||||
}
|
||||
}
|
||||
}
|
||||
isRollback = json['is_rollback'] as int?;
|
||||
hasMore = json['has_more'] as int?;
|
||||
triggerTime = json['trigger_time'] as int?;
|
||||
isNeedRefresh = json['is_need_refresh'] as int?;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'card_list': cardList?.map((e) => e.toJson()).toList(),
|
||||
'is_rollback': isRollback,
|
||||
'has_more': hasMore,
|
||||
'trigger_time': triggerTime,
|
||||
'is_need_refresh': isNeedRefresh,
|
||||
};
|
||||
}
|
||||
43
lib/models/live/live_feed_index/in_live.dart
Normal file
43
lib/models/live/live_feed_index/in_live.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
class InLive {
|
||||
int? alphaLight;
|
||||
int? alphaNight;
|
||||
String? animationUrl;
|
||||
String? animationUrlHash;
|
||||
String? backgroundColorLight;
|
||||
String? backgroundColorNight;
|
||||
String? fontColor;
|
||||
String? text;
|
||||
|
||||
InLive({
|
||||
this.alphaLight,
|
||||
this.alphaNight,
|
||||
this.animationUrl,
|
||||
this.animationUrlHash,
|
||||
this.backgroundColorLight,
|
||||
this.backgroundColorNight,
|
||||
this.fontColor,
|
||||
this.text,
|
||||
});
|
||||
|
||||
factory InLive.fromJson(Map<String, dynamic> json) => InLive(
|
||||
alphaLight: json['alpha_light'] as int?,
|
||||
alphaNight: json['alpha_night'] as int?,
|
||||
animationUrl: json['animation_url'] as String?,
|
||||
animationUrlHash: json['animation_url_hash'] as String?,
|
||||
backgroundColorLight: json['background_color_light'] as String?,
|
||||
backgroundColorNight: json['background_color_night'] as String?,
|
||||
fontColor: json['font_color'] as String?,
|
||||
text: json['text'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'alpha_light': alphaLight,
|
||||
'alpha_night': alphaNight,
|
||||
'animation_url': animationUrl,
|
||||
'animation_url_hash': animationUrlHash,
|
||||
'background_color_light': backgroundColorLight,
|
||||
'background_color_night': backgroundColorNight,
|
||||
'font_color': fontColor,
|
||||
'text': text,
|
||||
};
|
||||
}
|
||||
76
lib/models/live/live_feed_index/inline_live.dart
Normal file
76
lib/models/live/live_feed_index/inline_live.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'avatar.dart';
|
||||
import 'calendar_button.dart';
|
||||
import 'player_args.dart';
|
||||
import 'right_top_live_badge.dart';
|
||||
|
||||
class InlineLive {
|
||||
Avatar? avatar;
|
||||
String? cover;
|
||||
int? inlineStartDelayTime;
|
||||
int? inlineSustainDuration;
|
||||
String? link;
|
||||
PlayerArgs? playerArgs;
|
||||
dynamic rankListInfo;
|
||||
RightTopLiveBadge? rightTopLiveBadge;
|
||||
String? title;
|
||||
dynamic topViewInfo;
|
||||
String? upName;
|
||||
String? inlinePlayUrl;
|
||||
CalendarButton? calendarButton;
|
||||
|
||||
InlineLive({
|
||||
this.avatar,
|
||||
this.cover,
|
||||
this.inlineStartDelayTime,
|
||||
this.inlineSustainDuration,
|
||||
this.link,
|
||||
this.playerArgs,
|
||||
this.rankListInfo,
|
||||
this.rightTopLiveBadge,
|
||||
this.title,
|
||||
this.topViewInfo,
|
||||
this.upName,
|
||||
this.inlinePlayUrl,
|
||||
this.calendarButton,
|
||||
});
|
||||
|
||||
factory InlineLive.fromJson(Map<String, dynamic> json) => InlineLive(
|
||||
avatar: json['avatar'] == null
|
||||
? null
|
||||
: Avatar.fromJson(json['avatar'] as Map<String, dynamic>),
|
||||
cover: json['cover'] as String?,
|
||||
inlineStartDelayTime: json['inline_start_delay_time'] as int?,
|
||||
inlineSustainDuration: json['inline_sustain_duration'] as int?,
|
||||
link: json['link'] as String?,
|
||||
playerArgs: json['player_args'] == null
|
||||
? null
|
||||
: PlayerArgs.fromJson(json['player_args'] as Map<String, dynamic>),
|
||||
rankListInfo: json['rank_list_info'] as dynamic,
|
||||
rightTopLiveBadge: json['right_top_live_badge'] == null
|
||||
? null
|
||||
: RightTopLiveBadge.fromJson(json['right_top_live_badge'] as Map<String, dynamic>),
|
||||
title: json['title'] as String?,
|
||||
topViewInfo: json['top_view_info'] as dynamic,
|
||||
upName: json['up_name'] as String?,
|
||||
inlinePlayUrl: json['inline_play_url'] as String?,
|
||||
calendarButton: json['calendar_button'] == null
|
||||
? null
|
||||
: CalendarButton.fromJson(json['calendar_button'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'avatar': avatar?.toJson(),
|
||||
'cover': cover,
|
||||
'inline_start_delay_time': inlineStartDelayTime,
|
||||
'inline_sustain_duration': inlineSustainDuration,
|
||||
'link': link,
|
||||
'player_args': playerArgs?.toJson(),
|
||||
'rank_list_info': rankListInfo,
|
||||
'right_top_live_badge': rightTopLiveBadge?.toJson(),
|
||||
'title': title,
|
||||
'top_view_info': topViewInfo,
|
||||
'up_name': upName,
|
||||
'inline_play_url': inlinePlayUrl,
|
||||
'calendar_button': calendarButton?.toJson(),
|
||||
};
|
||||
}
|
||||
26
lib/models/live/live_feed_index/live_feed_index.dart
Normal file
26
lib/models/live/live_feed_index/live_feed_index.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'data.dart';
|
||||
|
||||
class LiveFeedIndex {
|
||||
int? code;
|
||||
String? message;
|
||||
int? ttl;
|
||||
LiveIndexData? data;
|
||||
|
||||
LiveFeedIndex({this.code, this.message, this.ttl, this.data});
|
||||
|
||||
factory LiveFeedIndex.fromJson(Map<String, dynamic> json) => LiveFeedIndex(
|
||||
code: json['code'] as int?,
|
||||
message: json['message'] as String?,
|
||||
ttl: json['ttl'] as int?,
|
||||
data: json['data'] == null
|
||||
? null
|
||||
: LiveIndexData.fromJson(json['data'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'code': code,
|
||||
'message': message,
|
||||
'ttl': ttl,
|
||||
'data': data?.toJson(),
|
||||
};
|
||||
}
|
||||
39
lib/models/live/live_feed_index/module_info.dart
Normal file
39
lib/models/live/live_feed_index/module_info.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
class ModuleInfo {
|
||||
int? id;
|
||||
String? link;
|
||||
String? pic;
|
||||
String? title;
|
||||
int? type;
|
||||
int? sort;
|
||||
int? count;
|
||||
|
||||
ModuleInfo({
|
||||
this.id,
|
||||
this.link,
|
||||
this.pic,
|
||||
this.title,
|
||||
this.type,
|
||||
this.sort,
|
||||
this.count,
|
||||
});
|
||||
|
||||
factory ModuleInfo.fromJson(Map<String, dynamic> json) => ModuleInfo(
|
||||
id: json['id'] as int?,
|
||||
link: json['link'] as String?,
|
||||
pic: json['pic'] as String?,
|
||||
title: json['title'] as String?,
|
||||
type: json['type'] as int?,
|
||||
sort: json['sort'] as int?,
|
||||
count: json['count'] as int?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'link': link,
|
||||
'pic': pic,
|
||||
'title': title,
|
||||
'type': type,
|
||||
'sort': sort,
|
||||
'count': count,
|
||||
};
|
||||
}
|
||||
39
lib/models/live/live_feed_index/player_args.dart
Normal file
39
lib/models/live/live_feed_index/player_args.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
class PlayerArgs {
|
||||
int? canPlay;
|
||||
bool? hideDanmuSwitch;
|
||||
int? liveStatus;
|
||||
int? parentAreaId;
|
||||
int? areaId;
|
||||
int? roomId;
|
||||
int? upId;
|
||||
|
||||
PlayerArgs({
|
||||
this.canPlay,
|
||||
this.hideDanmuSwitch,
|
||||
this.liveStatus,
|
||||
this.parentAreaId,
|
||||
this.areaId,
|
||||
this.roomId,
|
||||
this.upId,
|
||||
});
|
||||
|
||||
factory PlayerArgs.fromJson(Map<String, dynamic> json) => PlayerArgs(
|
||||
canPlay: json['can_play'] as int?,
|
||||
hideDanmuSwitch: json['hide_danmu_switch'] as bool?,
|
||||
liveStatus: json['live_status'] as int?,
|
||||
parentAreaId: json['parent_area_id'] as int?,
|
||||
areaId: json['area_id'] as int?,
|
||||
roomId: json['room_id'] as int?,
|
||||
upId: json['up_id'] as int?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'can_play': canPlay,
|
||||
'hide_danmu_switch': hideDanmuSwitch,
|
||||
'live_status': liveStatus,
|
||||
'parent_area_id': parentAreaId,
|
||||
'area_id': areaId,
|
||||
'room_id': roomId,
|
||||
'up_id': upId,
|
||||
};
|
||||
}
|
||||
20
lib/models/live/live_feed_index/quality_description.dart
Normal file
20
lib/models/live/live_feed_index/quality_description.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
class QualityDescription {
|
||||
int? qn;
|
||||
String? desc;
|
||||
|
||||
QualityDescription({this.qn, this.desc});
|
||||
|
||||
factory QualityDescription.fromJson(Map<String, dynamic> json) {
|
||||
return QualityDescription(
|
||||
qn: json['qn'] as int?,
|
||||
desc: json['desc'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'qn': qn,
|
||||
'desc': desc,
|
||||
};
|
||||
}
|
||||
24
lib/models/live/live_feed_index/right_top_live_badge.dart
Normal file
24
lib/models/live/live_feed_index/right_top_live_badge.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'in_live.dart';
|
||||
|
||||
class RightTopLiveBadge {
|
||||
InLive? inLive;
|
||||
int? liveStatus;
|
||||
|
||||
RightTopLiveBadge({this.inLive, this.liveStatus});
|
||||
|
||||
factory RightTopLiveBadge.fromJson(Map<String, dynamic> json) {
|
||||
return RightTopLiveBadge(
|
||||
inLive: json['in_live'] == null
|
||||
? null
|
||||
: InLive.fromJson(json['in_live'] as Map<String, dynamic>),
|
||||
liveStatus: json['live_status'] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'in_live': inLive?.toJson(),
|
||||
'live_status': liveStatus,
|
||||
};
|
||||
}
|
||||
39
lib/models/live/live_feed_index/watched_show.dart
Normal file
39
lib/models/live/live_feed_index/watched_show.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
class WatchedShow {
|
||||
bool? sw1tch;
|
||||
int? num;
|
||||
String? textSmall;
|
||||
String? textLarge;
|
||||
String? icon;
|
||||
int? iconLocation;
|
||||
String? iconWeb;
|
||||
|
||||
WatchedShow({
|
||||
this.sw1tch,
|
||||
this.num,
|
||||
this.textSmall,
|
||||
this.textLarge,
|
||||
this.icon,
|
||||
this.iconLocation,
|
||||
this.iconWeb,
|
||||
});
|
||||
|
||||
factory WatchedShow.fromJson(Map<String, dynamic> json) => WatchedShow(
|
||||
sw1tch: json['switch'] as bool?,
|
||||
num: json['num'] as int?,
|
||||
textSmall: json['text_small'] as String?,
|
||||
textLarge: json['text_large'] as String?,
|
||||
icon: json['icon'] as String?,
|
||||
iconLocation: json['icon_location'] as int?,
|
||||
iconWeb: json['icon_web'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'switch': sw1tch,
|
||||
'num': num,
|
||||
'text_small': textSmall,
|
||||
'text_large': textLarge,
|
||||
'icon': icon,
|
||||
'icon_location': iconLocation,
|
||||
'icon_web': iconWeb,
|
||||
};
|
||||
}
|
||||
52
lib/models/live/live_follow/data.dart
Normal file
52
lib/models/live/live_follow/data.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'item.dart';
|
||||
|
||||
class LiveFollowData {
|
||||
String? title;
|
||||
int? pageSize;
|
||||
int? totalPage;
|
||||
List<LiveFollowItem>? list;
|
||||
int? count;
|
||||
int? neverLivedCount;
|
||||
int? liveCount;
|
||||
List<dynamic>? neverLivedFaces;
|
||||
|
||||
LiveFollowData({
|
||||
this.title,
|
||||
this.pageSize,
|
||||
this.totalPage,
|
||||
this.list,
|
||||
this.count,
|
||||
this.neverLivedCount,
|
||||
this.liveCount,
|
||||
this.neverLivedFaces,
|
||||
});
|
||||
|
||||
LiveFollowData.fromJson(Map<String, dynamic> json) {
|
||||
title = json['title'] as String?;
|
||||
pageSize = json['pageSize'] as int?;
|
||||
totalPage = json['totalPage'] as int?;
|
||||
if ((json['list'] as List<dynamic>?)?.isNotEmpty == true) {
|
||||
list = <LiveFollowItem>[];
|
||||
for (var json in json['list']) {
|
||||
if (json['live_status'] == 1) {
|
||||
list!.add(LiveFollowItem.fromJson(json));
|
||||
}
|
||||
}
|
||||
}
|
||||
count = json['count'] as int?;
|
||||
neverLivedCount = json['never_lived_count'] as int?;
|
||||
liveCount = json['live_count'] as int?;
|
||||
neverLivedFaces = json['never_lived_faces'] as List<dynamic>?;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'title': title,
|
||||
'pageSize': pageSize,
|
||||
'totalPage': totalPage,
|
||||
'list': list?.map((e) => e.toJson()).toList(),
|
||||
'count': count,
|
||||
'never_lived_count': neverLivedCount,
|
||||
'live_count': liveCount,
|
||||
'never_lived_faces': neverLivedFaces,
|
||||
};
|
||||
}
|
||||
111
lib/models/live/live_follow/item.dart
Normal file
111
lib/models/live/live_follow/item.dart
Normal file
@@ -0,0 +1,111 @@
|
||||
class LiveFollowItem {
|
||||
int? roomid;
|
||||
int? uid;
|
||||
String? uname;
|
||||
String? title;
|
||||
String? face;
|
||||
int? liveStatus;
|
||||
int? recordNum;
|
||||
String? recentRecordId;
|
||||
int? isAttention;
|
||||
int? clipnum;
|
||||
int? fansNum;
|
||||
String? areaName;
|
||||
String? areaValue;
|
||||
String? tags;
|
||||
String? recentRecordIdV2;
|
||||
int? recordNumV2;
|
||||
int? recordLiveTime;
|
||||
String? areaNameV2;
|
||||
String? roomNews;
|
||||
bool? sw1tch;
|
||||
String? watchIcon;
|
||||
String? textSmall;
|
||||
String? roomCover;
|
||||
int? parentAreaId;
|
||||
int? areaId;
|
||||
|
||||
LiveFollowItem({
|
||||
this.roomid,
|
||||
this.uid,
|
||||
this.uname,
|
||||
this.title,
|
||||
this.face,
|
||||
this.liveStatus,
|
||||
this.recordNum,
|
||||
this.recentRecordId,
|
||||
this.isAttention,
|
||||
this.clipnum,
|
||||
this.fansNum,
|
||||
this.areaName,
|
||||
this.areaValue,
|
||||
this.tags,
|
||||
this.recentRecordIdV2,
|
||||
this.recordNumV2,
|
||||
this.recordLiveTime,
|
||||
this.areaNameV2,
|
||||
this.roomNews,
|
||||
this.sw1tch,
|
||||
this.watchIcon,
|
||||
this.textSmall,
|
||||
this.roomCover,
|
||||
this.parentAreaId,
|
||||
this.areaId,
|
||||
});
|
||||
|
||||
factory LiveFollowItem.fromJson(Map<String, dynamic> json) => LiveFollowItem(
|
||||
roomid: json['roomid'] as int?,
|
||||
uid: json['uid'] as int?,
|
||||
uname: json['uname'] as String?,
|
||||
title: json['title'] as String?,
|
||||
face: json['face'] as String?,
|
||||
liveStatus: json['live_status'] as int?,
|
||||
recordNum: json['record_num'] as int?,
|
||||
recentRecordId: json['recent_record_id'] as String?,
|
||||
isAttention: json['is_attention'] as int?,
|
||||
clipnum: json['clipnum'] as int?,
|
||||
fansNum: json['fans_num'] as int?,
|
||||
areaName: json['area_name'] as String?,
|
||||
areaValue: json['area_value'] as String?,
|
||||
tags: json['tags'] as String?,
|
||||
recentRecordIdV2: json['recent_record_id_v2'] as String?,
|
||||
recordNumV2: json['record_num_v2'] as int?,
|
||||
recordLiveTime: json['record_live_time'] as int?,
|
||||
areaNameV2: json['area_name_v2'] as String?,
|
||||
roomNews: json['room_news'] as String?,
|
||||
sw1tch: json['switch'] as bool?,
|
||||
watchIcon: json['watch_icon'] as String?,
|
||||
textSmall: json['text_small'] as String?,
|
||||
roomCover: json['room_cover'] as String?,
|
||||
parentAreaId: json['parent_area_id'] as int?,
|
||||
areaId: json['area_id'] as int?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'roomid': roomid,
|
||||
'uid': uid,
|
||||
'uname': uname,
|
||||
'title': title,
|
||||
'face': face,
|
||||
'live_status': liveStatus,
|
||||
'record_num': recordNum,
|
||||
'recent_record_id': recentRecordId,
|
||||
'is_attention': isAttention,
|
||||
'clipnum': clipnum,
|
||||
'fans_num': fansNum,
|
||||
'area_name': areaName,
|
||||
'area_value': areaValue,
|
||||
'tags': tags,
|
||||
'recent_record_id_v2': recentRecordIdV2,
|
||||
'record_num_v2': recordNumV2,
|
||||
'record_live_time': recordLiveTime,
|
||||
'area_name_v2': areaNameV2,
|
||||
'room_news': roomNews,
|
||||
'switch': sw1tch,
|
||||
'watch_icon': watchIcon,
|
||||
'text_small': textSmall,
|
||||
'room_cover': roomCover,
|
||||
'parent_area_id': parentAreaId,
|
||||
'area_id': areaId,
|
||||
};
|
||||
}
|
||||
@@ -1,109 +1,26 @@
|
||||
import 'package:PiliPlus/http/live.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/live/follow.dart';
|
||||
import 'package:PiliPlus/models/live/item.dart';
|
||||
import 'package:PiliPlus/models/live/live_feed_index/card_list.dart';
|
||||
import 'package:PiliPlus/models/live/live_feed_index/data.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:get/get_rx/src/rx_types/rx_types.dart';
|
||||
|
||||
class LiveController
|
||||
extends CommonListController<List<LiveItemModel>?, LiveItemModel> {
|
||||
class LiveController extends CommonListController<LiveIndexData, LiveCardList> {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
if (isLogin.value) {
|
||||
fetchLiveFollowing();
|
||||
}
|
||||
}
|
||||
|
||||
String? gaiaVtoken;
|
||||
final RxBool isLogin = Accounts.main.isLogin.obs;
|
||||
|
||||
@override
|
||||
Future<LoadingState<List<LiveItemModel>?>> customGetData() =>
|
||||
LiveHttp.liveList(pn: currentPage, gaiaVtoken: gaiaVtoken);
|
||||
|
||||
@override
|
||||
bool handleError(String? errMsg) {
|
||||
if (errMsg?.startsWith('voucher') == true) {
|
||||
loadingState.value = LoadingState.error(' -352 ');
|
||||
RequestUtils.validate(
|
||||
errMsg!,
|
||||
(gaiaVtoken) {
|
||||
this.gaiaVtoken = gaiaVtoken;
|
||||
onReload();
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
List<LiveCardList>? getDataList(LiveIndexData response) {
|
||||
return response.cardList;
|
||||
}
|
||||
|
||||
@override
|
||||
Future onRefresh() {
|
||||
fetchLiveFollowing();
|
||||
return super.onRefresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Future onReload() {
|
||||
if (loadingState.value is Error) {
|
||||
String? errMsg = (loadingState.value as Error).errMsg;
|
||||
if (errMsg == '-352') {
|
||||
gaiaVtoken = null;
|
||||
}
|
||||
}
|
||||
return super.onReload();
|
||||
}
|
||||
|
||||
late RxBool isLogin = Accounts.main.isLogin.obs;
|
||||
late Rx<LoadingState<List<LiveFollowingItemModel>?>> followListState =
|
||||
Rx(LoadingState.loading());
|
||||
late int followPage = 1;
|
||||
late bool followEnd = false;
|
||||
late RxInt liveCount = 0.obs;
|
||||
|
||||
Future fetchLiveFollowing([bool isRefresh = true]) async {
|
||||
if (!isLogin.value || (isRefresh.not && followEnd)) {
|
||||
return;
|
||||
}
|
||||
if (isRefresh) {
|
||||
followPage = 1;
|
||||
followEnd = false;
|
||||
}
|
||||
dynamic res = await LiveHttp.liveFollowing(pn: followPage, ps: 20);
|
||||
if (res['status']) {
|
||||
LiveFollowingModel data = res['data'];
|
||||
liveCount.value = data.liveCount ?? 0;
|
||||
List<LiveFollowingItemModel>? dataList = data.list
|
||||
?.where((LiveFollowingItemModel item) =>
|
||||
item.liveStatus == 1 && item.recordLiveTime == 0)
|
||||
.toList();
|
||||
if (dataList.isNullOrEmpty) {
|
||||
followEnd = true;
|
||||
if (isRefresh) {
|
||||
followListState.value = LoadingState.success(dataList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isRefresh) {
|
||||
if (dataList!.length >= liveCount.value) {
|
||||
followEnd = true;
|
||||
}
|
||||
followListState.value = LoadingState.success(dataList);
|
||||
} else if (followListState.value is Success) {
|
||||
List<LiveFollowingItemModel> list = followListState.value.data!
|
||||
..addAll(dataList!);
|
||||
if (list.length >= liveCount.value) {
|
||||
followEnd = true;
|
||||
}
|
||||
followListState.refresh();
|
||||
}
|
||||
followPage++;
|
||||
} else if (isRefresh) {
|
||||
followListState.value = LoadingState.error(res['msg']);
|
||||
}
|
||||
}
|
||||
Future<LoadingState<LiveIndexData>> customGetData() =>
|
||||
LiveHttp.liveFeedIndex(pn: currentPage, isLogin: isLogin.value);
|
||||
}
|
||||
|
||||
@@ -2,17 +2,17 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/skeleton/video_card_v.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/loading_widget/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/live/item.dart';
|
||||
import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart';
|
||||
import 'package:PiliPlus/models/live/live_feed_index/card_list.dart';
|
||||
import 'package:PiliPlus/pages/common/common_page.dart';
|
||||
import 'package:PiliPlus/pages/live/controller.dart';
|
||||
import 'package:PiliPlus/pages/live/widgets/live_item.dart';
|
||||
import 'package:PiliPlus/pages/live/widgets/live_item_follow.dart';
|
||||
import 'package:PiliPlus/pages/live/widgets/live_item_app.dart';
|
||||
import 'package:PiliPlus/pages/live_follow/view.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -49,18 +49,6 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
controller: controller.scrollController,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
Obx(
|
||||
() => controller.isLogin.value
|
||||
? SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: StyleString.cardSpace,
|
||||
),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: _buildFollowList(),
|
||||
),
|
||||
)
|
||||
: const SliverToBoxAdapter(),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.cardSpace,
|
||||
@@ -74,7 +62,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState<List<LiveItemModel>?> loadingState) {
|
||||
Widget _buildBody(LoadingState<List<LiveCardList>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
@@ -92,25 +80,48 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
),
|
||||
),
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: MediaQuery.textScalerOf(context).scale(90),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
return LiveCardV(liveItem: loadingState.response![index]);
|
||||
},
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
)
|
||||
: scrollErrorWidget(onReload: controller.onReload),
|
||||
? Builder(builder: (context) {
|
||||
List<LiveCardList> list = loadingState.response!;
|
||||
LiveCardList? followItem;
|
||||
if (list.first.cardType == 'my_idol_v1') {
|
||||
followItem = list.first;
|
||||
list = list.sublist(1);
|
||||
}
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
if (followItem != null)
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: _buildFollowList(followItem),
|
||||
),
|
||||
),
|
||||
if (list.isNotEmpty)
|
||||
SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent:
|
||||
MediaQuery.textScalerOf(context).scale(90),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == list.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
return LiveCardVApp(
|
||||
item: list[index].cardData!.smallCardV1!,
|
||||
);
|
||||
},
|
||||
childCount: list.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
})
|
||||
: HttpError(onReload: controller.onReload),
|
||||
Error() => HttpError(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: controller.onReload,
|
||||
@@ -118,7 +129,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
};
|
||||
}
|
||||
|
||||
Widget _buildFollowList() {
|
||||
Widget _buildFollowList(LiveCardList item) {
|
||||
final theme = Theme.of(context);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -126,33 +137,32 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
const TextSpan(text: '我的关注 '),
|
||||
TextSpan(
|
||||
text: '${controller.liveCount.value}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
const TextSpan(text: '我的关注 '),
|
||||
TextSpan(
|
||||
text:
|
||||
'${item.cardData?.myIdolV1?.extraInfo?.totalCount ?? 0}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
TextSpan(
|
||||
text: '人正在直播',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '人正在直播',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(_buildFollowListPage);
|
||||
Get.to(const LiveFollowPage());
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -173,161 +183,69 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
),
|
||||
],
|
||||
),
|
||||
Obx(() => _buildFollowBody(theme, controller.followListState.value)),
|
||||
_buildFollowBody(theme, item.cardData?.myIdolV1?.list),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFollowBody(ThemeData theme, LoadingState loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SizedBox(
|
||||
height: 80,
|
||||
child: loadingWidget,
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
? MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeLeft: context.orientation == Orientation.landscape,
|
||||
child: SelfSizedHorizontalList(
|
||||
gapSize: 5,
|
||||
childBuilder: (index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
controller.fetchLiveFollowing(false);
|
||||
}
|
||||
return SizedBox(
|
||||
width: 65,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
'/liveRoom?roomid=${loadingState.response[index].roomId}',
|
||||
);
|
||||
},
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
Get.toNamed(
|
||||
'/member?mid=${loadingState.response[index].uid}',
|
||||
arguments: {
|
||||
'face': loadingState.response[index].face,
|
||||
'heroTag': Utils.makeHeroTag(
|
||||
loadingState.response[index].uid)
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(2),
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1.5,
|
||||
color: theme.colorScheme.primary,
|
||||
strokeAlign: BorderSide.strokeAlignOutside,
|
||||
),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
width: 45,
|
||||
height: 45,
|
||||
src: loadingState.response[index].face,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
loadingState.response[index].uname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: loadingState.response.length,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
Error() => GestureDetector(
|
||||
onTap: () {
|
||||
controller.fetchLiveFollowing();
|
||||
},
|
||||
child: Container(
|
||||
height: 80,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
loadingState.errMsg,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Widget get _buildFollowListPage => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Obx(
|
||||
() => Text('${controller.liveCount.value}人正在直播'),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
Obx(() => _buildFollowListBody(controller.followListState.value)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildFollowListBody(LoadingState loadingState) {
|
||||
return switch (loadingState) {
|
||||
Success() || Loading() => SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace,
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: MediaQuery.textScalerOf(context).scale(90),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
if (loadingState is Success &&
|
||||
index == loadingState.response.length - 1) {
|
||||
controller.fetchLiveFollowing(false);
|
||||
}
|
||||
return loadingState is Success
|
||||
? LiveCardVFollow(
|
||||
liveItem: loadingState.response[index],
|
||||
)
|
||||
: const VideoCardVSkeleton();
|
||||
Widget _buildFollowBody(ThemeData theme, List<CardLiveItem>? list) {
|
||||
if (list.isNullOrEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeLeft: context.orientation == Orientation.landscape,
|
||||
child: SelfSizedHorizontalList(
|
||||
gapSize: 5,
|
||||
childBuilder: (index) {
|
||||
final item = list[index];
|
||||
return SizedBox(
|
||||
width: 65,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.toNamed('/liveRoom?roomid=${item.roomid}');
|
||||
},
|
||||
childCount:
|
||||
loadingState is Success ? loadingState.response.length : 10,
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
Get.toNamed('/member?mid=${item.uid}');
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(2),
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1.5,
|
||||
color: theme.colorScheme.primary,
|
||||
strokeAlign: BorderSide.strokeAlignOutside,
|
||||
),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
width: 45,
|
||||
height: 45,
|
||||
src: item.face,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.uname!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Error() => HttpError(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: () {
|
||||
controller
|
||||
..followListState.value = LoadingState.loading()
|
||||
..fetchLiveFollowing();
|
||||
},
|
||||
),
|
||||
};
|
||||
);
|
||||
},
|
||||
itemCount: list!.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
143
lib/pages/live/widgets/live_item_app.dart
Normal file
143
lib/pages/live/widgets/live_item_app.dart
Normal file
@@ -0,0 +1,143 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class LiveCardVApp extends StatelessWidget {
|
||||
final CardLiveItem item;
|
||||
|
||||
const LiveCardVApp({
|
||||
super.key,
|
||||
required this.item,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String heroTag = Utils.makeHeroTag(item.id);
|
||||
return Card(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
margin: EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Get.toNamed('/liveRoom?roomid=${item.id}');
|
||||
},
|
||||
onLongPress: () => imageSaveDialog(
|
||||
title: item.title,
|
||||
cover: item.cover,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||
double maxWidth = boxConstraints.maxWidth;
|
||||
double maxHeight = boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
src: item.cover!,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: AnimatedOpacity(
|
||||
opacity: 1,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: videoStat(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
liveContent(context)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget liveContent(context) {
|
||||
final theme = Theme.of(context);
|
||||
return Expanded(
|
||||
flex: 1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(5, 8, 5, 4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${item.title}',
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${item.uname}',
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: theme.textTheme.labelMedium!.fontSize,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoStat(context) {
|
||||
return Container(
|
||||
height: 50,
|
||||
padding: const EdgeInsets.only(top: 26, left: 10, right: 10),
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[
|
||||
Colors.transparent,
|
||||
Colors.black54,
|
||||
],
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${item.areaName}',
|
||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||
),
|
||||
if (item.watchedShow?.textSmall != null)
|
||||
Text(
|
||||
'${Utils.numFormat(item.watchedShow!.textSmall)}围观',
|
||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
34
lib/pages/live_follow/controller.dart
Normal file
34
lib/pages/live_follow/controller.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:PiliPlus/http/live.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/live/live_follow/data.dart';
|
||||
import 'package:PiliPlus/models/live/live_follow/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class LiveFollowController
|
||||
extends CommonListController<LiveFollowData, LiveFollowItem> {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
Rx<int?> count = Rx<int?>(null);
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (count.value != null && length >= count.value!) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<LiveFollowItem>? getDataList(LiveFollowData response) {
|
||||
count.value = response.liveCount;
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<LiveFollowData>> customGetData() =>
|
||||
LiveHttp.liveFollow(currentPage);
|
||||
}
|
||||
102
lib/pages/live_follow/view.dart
Normal file
102
lib/pages/live_follow/view.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/skeleton/video_card_v.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/live/live_follow/item.dart';
|
||||
import 'package:PiliPlus/pages/live_follow/controller.dart';
|
||||
import 'package:PiliPlus/pages/live_follow/widgets/live_item_follow.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class LiveFollowPage extends StatefulWidget {
|
||||
const LiveFollowPage({super.key});
|
||||
|
||||
@override
|
||||
State<LiveFollowPage> createState() => _LiveFollowPageState();
|
||||
}
|
||||
|
||||
class _LiveFollowPageState extends State<LiveFollowPage> {
|
||||
final _controller = Get.put(LiveFollowController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Obx(
|
||||
() => Text(_controller.count.value != null
|
||||
? '${_controller.count.value}人正在直播'
|
||||
: '关注直播'),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: Obx(() => _buildBody(_controller.loadingState.value)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState<List<LiveFollowItem>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: MediaQuery.textScalerOf(context).scale(90),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return const VideoCardVSkeleton();
|
||||
},
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: MediaQuery.textScalerOf(context).scale(90),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return LiveCardVFollow(
|
||||
liveItem: loadingState.response![index],
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
)
|
||||
: HttpError(onReload: _controller.onReload),
|
||||
Error() => HttpError(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/live/follow.dart';
|
||||
import 'package:PiliPlus/models/live/live_follow/item.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class LiveCardVFollow extends StatelessWidget {
|
||||
final LiveFollowingItemModel liveItem;
|
||||
final LiveFollowItem liveItem;
|
||||
|
||||
const LiveCardVFollow({
|
||||
super.key,
|
||||
@@ -17,13 +17,13 @@ class LiveCardVFollow extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String heroTag = Utils.makeHeroTag(liveItem.roomId);
|
||||
String heroTag = Utils.makeHeroTag(liveItem.roomid);
|
||||
return Card(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
margin: EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Get.toNamed('/liveRoom?roomid=${liveItem.roomId}');
|
||||
Get.toNamed('/liveRoom?roomid=${liveItem.roomid}');
|
||||
},
|
||||
onLongPress: () => imageSaveDialog(
|
||||
title: liveItem.title,
|
||||
@@ -85,9 +85,7 @@ class LoginUtils {
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
Get.find<LiveController>()
|
||||
..isLogin.value = true
|
||||
..fetchLiveFollowing();
|
||||
Get.find<LiveController>().isLogin.value = true;
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
@@ -152,9 +150,7 @@ class LoginUtils {
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
Get.find<LiveController>()
|
||||
..isLogin.value = false
|
||||
..followListState.value = LoadingState.loading();
|
||||
Get.find<LiveController>().isLogin.value = false;
|
||||
} catch (_) {}
|
||||
|
||||
for (int i = 0; i < tabsConfig.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user