mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
refa: query data (#659)
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -50,7 +50,7 @@ class BangumiHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> bangumiList({
|
||||
static Future<LoadingState<List<BangumiListItemModel>?>> bangumiList({
|
||||
int? page,
|
||||
int? indexType,
|
||||
}) async {
|
||||
@@ -67,7 +67,7 @@ class BangumiHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> bangumiFollowList({
|
||||
static Future<LoadingState<BangumiListDataModel>> bangumiFollowList({
|
||||
required dynamic mid,
|
||||
required int type,
|
||||
required int pn,
|
||||
@@ -80,12 +80,8 @@ class BangumiHttp {
|
||||
'pn': pn,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
BangumiListDataModel data =
|
||||
BangumiListDataModel.fromJson(res.data['data']);
|
||||
if (followStatus != null) {
|
||||
return LoadingState.success(data.list);
|
||||
}
|
||||
return LoadingState.success(data);
|
||||
return LoadingState.success(
|
||||
BangumiListDataModel.fromJson(res.data['data']));
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import '../models/user/black.dart';
|
||||
import 'index.dart';
|
||||
|
||||
class BlackHttp {
|
||||
static Future<LoadingState> blackList({required int pn, int? ps}) async {
|
||||
static Future<LoadingState<BlackListDataModel>> blackList(
|
||||
{required int pn, int? ps}) async {
|
||||
var res = await Request().get(Api.blackLst, queryParameters: {
|
||||
'pn': pn,
|
||||
'ps': ps ?? 50,
|
||||
|
||||
@@ -8,7 +8,7 @@ import '../models/dynamics/up.dart';
|
||||
import 'index.dart';
|
||||
|
||||
class DynamicsHttp {
|
||||
static Future<LoadingState> followDynamic({
|
||||
static Future<LoadingState<DynamicsDataModel>> followDynamic({
|
||||
String? type,
|
||||
String? offset,
|
||||
int? mid,
|
||||
|
||||
@@ -4,7 +4,7 @@ import '../models/fans/result.dart';
|
||||
import 'index.dart';
|
||||
|
||||
class FanHttp {
|
||||
static Future<LoadingState> fans(
|
||||
static Future<LoadingState<FansDataModel>> fans(
|
||||
{int? vmid, int? pn, int? ps, String? orderType}) async {
|
||||
var res = await Request().get(Api.fans, queryParameters: {
|
||||
'vmid': vmid,
|
||||
|
||||
@@ -10,13 +10,13 @@ import 'api.dart';
|
||||
import 'init.dart';
|
||||
|
||||
class LiveHttp {
|
||||
static Future<LoadingState> liveList(
|
||||
static Future<LoadingState<List<LiveItemModel>?>> liveList(
|
||||
{int? vmid, int? pn, int? ps, String? orderType}) async {
|
||||
var res = await Request().get(Api.liveList,
|
||||
queryParameters: {'page': pn, 'page_size': 30, 'platform': 'web'});
|
||||
if (res.data['code'] == 0) {
|
||||
List<LiveItemModel> list = res.data['data']['list']
|
||||
.map<LiveItemModel>((e) => LiveItemModel.fromJson(e))
|
||||
List<LiveItemModel>? list = (res.data['data']?['list'] as List?)
|
||||
?.map<LiveItemModel>((e) => LiveItemModel.fromJson(e))
|
||||
.toList();
|
||||
return LoadingState.success(list);
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,9 @@ import 'package:PiliPlus/grpc/grpc_repo.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space/data.dart';
|
||||
import 'package:PiliPlus/models/space_archive/data.dart' as space_archive;
|
||||
import 'package:PiliPlus/models/space_article/data.dart' as space_article;
|
||||
import 'package:PiliPlus/models/space/data.dart' as space_;
|
||||
import 'package:PiliPlus/models/space_fav/space_fav.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
||||
show ContributeType;
|
||||
@@ -62,7 +65,7 @@ class MemberHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> spaceArticle({
|
||||
static Future<LoadingState<space_article.Data>> spaceArticle({
|
||||
required int mid,
|
||||
required int page,
|
||||
}) async {
|
||||
@@ -138,13 +141,13 @@ class MemberHttp {
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(res.data['data']['items_lists']);
|
||||
return LoadingState.success(res.data['data']?['items_lists']);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> spaceArchive({
|
||||
static Future<LoadingState<space_archive.Data>> spaceArchive({
|
||||
required ContributeType type,
|
||||
required int? mid,
|
||||
String? aid,
|
||||
@@ -241,7 +244,7 @@ class MemberHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> space({
|
||||
static Future<LoadingState<space_.Data>> space({
|
||||
int? mid,
|
||||
dynamic fromViewAid,
|
||||
}) async {
|
||||
@@ -391,7 +394,7 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
// 用户动态
|
||||
static Future<LoadingState> memberDynamic({
|
||||
static Future<LoadingState<DynamicsDataModel>> memberDynamic({
|
||||
String? offset,
|
||||
int? mid,
|
||||
}) async {
|
||||
@@ -601,7 +604,8 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
// 最近投币
|
||||
static Future<LoadingState> getRecentCoinVideo({required int mid}) async {
|
||||
static Future<LoadingState<List<MemberCoinsDataModel>?>> getRecentCoinVideo(
|
||||
{required int mid}) async {
|
||||
Map params = await WbiSign.makSign({
|
||||
'mid': mid,
|
||||
'gaia_source': 'main_web',
|
||||
@@ -618,16 +622,18 @@ class MemberHttp {
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(res.data['data']
|
||||
.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
||||
.toList());
|
||||
List<MemberCoinsDataModel>? list = (res.data['data'] as List?)
|
||||
?.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
||||
.toList();
|
||||
return LoadingState.success(list);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
// 最近点赞
|
||||
static Future<LoadingState> getRecentLikeVideo({required int mid}) async {
|
||||
static Future<LoadingState<List<MemberCoinsDataModel>?>> getRecentLikeVideo(
|
||||
{required int mid}) async {
|
||||
Map params = await WbiSign.makSign({
|
||||
'mid': mid,
|
||||
'gaia_source': 'main_web',
|
||||
@@ -644,9 +650,10 @@ class MemberHttp {
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(res.data['data']['list']
|
||||
.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
||||
.toList());
|
||||
List<MemberCoinsDataModel>? list = (res.data['data']?['list'] as List?)
|
||||
?.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
||||
.toList();
|
||||
return LoadingState.success(list);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import 'api.dart';
|
||||
import 'init.dart';
|
||||
|
||||
class MsgHttp {
|
||||
static Future<LoadingState> msgFeedReplyMe(
|
||||
static Future<LoadingState<MsgFeedReplyMe>> msgFeedReplyMe(
|
||||
{int cursor = -1, int cursorTime = -1}) async {
|
||||
var res = await Request().get(Api.msgFeedReply, queryParameters: {
|
||||
'id': cursor == -1 ? null : cursor,
|
||||
@@ -34,7 +34,7 @@ class MsgHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> msgFeedAtMe(
|
||||
static Future<LoadingState<MsgFeedAtMe>> msgFeedAtMe(
|
||||
{int cursor = -1, int cursorTime = -1}) async {
|
||||
var res = await Request().get(Api.msgFeedAt, queryParameters: {
|
||||
'id': cursor == -1 ? null : cursor,
|
||||
@@ -51,7 +51,7 @@ class MsgHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> msgFeedLikeMe(
|
||||
static Future<LoadingState<MsgFeedLikeMe>> msgFeedLikeMe(
|
||||
{int cursor = -1, int cursorTime = -1}) async {
|
||||
var res = await Request().get(Api.msgFeedLike, queryParameters: {
|
||||
'id': cursor == -1 ? null : cursor,
|
||||
@@ -68,7 +68,7 @@ class MsgHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> msgFeedNotify(
|
||||
static Future<LoadingState<List<SystemNotifyList>?>> msgFeedNotify(
|
||||
{int cursor = -1, int pageSize = 20}) async {
|
||||
var res = await Request().get(Api.msgSysNotify, queryParameters: {
|
||||
'cursor': cursor == -1 ? null : cursor,
|
||||
|
||||
@@ -124,7 +124,7 @@ class ReplyHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> replyListGrpc({
|
||||
static Future<LoadingState<MainListReply>> replyListGrpc({
|
||||
int type = 1,
|
||||
required int oid,
|
||||
required CursorReq cursor,
|
||||
@@ -387,7 +387,8 @@ class ReplyHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> getEmoteList({String? business}) async {
|
||||
static Future<LoadingState<List<Packages>?>> getEmoteList(
|
||||
{String? business}) async {
|
||||
var res = await Request().get(Api.myEmote, queryParameters: {
|
||||
'business': business ?? 'reply',
|
||||
'web_location': '333.1245',
|
||||
|
||||
@@ -160,7 +160,8 @@ class SearchHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> bangumiInfoNew({int? seasonId, int? epId}) async {
|
||||
static Future<LoadingState<BangumiInfoModel>> bangumiInfoNew(
|
||||
{int? seasonId, int? epId}) async {
|
||||
final dynamic res = await Request().get(
|
||||
Api.bangumiInfo,
|
||||
queryParameters: {
|
||||
|
||||
@@ -46,7 +46,7 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 收藏夹
|
||||
static Future<LoadingState> userfavFolder({
|
||||
static Future<LoadingState<FavFolderData>> userfavFolder({
|
||||
required int pn,
|
||||
required int ps,
|
||||
required dynamic mid,
|
||||
@@ -170,7 +170,7 @@ class UserHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> userFavFolderDetail(
|
||||
static Future<LoadingState<FavDetailData>> userFavFolderDetail(
|
||||
{required int mediaId,
|
||||
required int pn,
|
||||
required int ps,
|
||||
@@ -195,7 +195,7 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 稍后再看
|
||||
static Future<LoadingState> seeYouLater() async {
|
||||
static Future<LoadingState<Map>> seeYouLater() async {
|
||||
var res = await Request().get(Api.seeYouLater);
|
||||
if (res.data['code'] == 0) {
|
||||
if (res.data['data']['count'] == 0) {
|
||||
@@ -220,7 +220,7 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 观看历史
|
||||
static Future<LoadingState> historyList({
|
||||
static Future<LoadingState<HistoryData>> historyList({
|
||||
required String type,
|
||||
int? max,
|
||||
int? viewAt,
|
||||
@@ -426,7 +426,7 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 我的订阅
|
||||
static Future<LoadingState> userSubFolder({
|
||||
static Future<LoadingState<List<SubFolderItemData>?>> userSubFolder({
|
||||
required int mid,
|
||||
required int pn,
|
||||
required int ps,
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:developer';
|
||||
import 'package:PiliPlus/grpc/app/card/v1/card.pb.dart' as card;
|
||||
import 'package:PiliPlus/grpc/grpc_repo.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/member/article.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -145,7 +146,7 @@ class VideoHttp {
|
||||
}
|
||||
|
||||
// 最热视频
|
||||
static Future<LoadingState> hotVideoList(
|
||||
static Future<LoadingState<List<HotVideoItemModel>>> hotVideoList(
|
||||
{required int pn, required int ps}) async {
|
||||
var res = await Request().get(
|
||||
Api.hotList,
|
||||
@@ -345,15 +346,16 @@ class VideoHttp {
|
||||
}
|
||||
|
||||
// 相关视频
|
||||
static Future<LoadingState> relatedVideoList({required String bvid}) async {
|
||||
static Future<LoadingState<List<HotVideoItemModel>?>> relatedVideoList(
|
||||
{required String bvid}) async {
|
||||
var res =
|
||||
await Request().get(Api.relatedList, queryParameters: {'bvid': bvid});
|
||||
if (res.data['code'] == 0) {
|
||||
final items =
|
||||
(res.data['data'] as List).map((i) => HotVideoItemModel.fromJson(i));
|
||||
final items = (res.data['data'] as List?)
|
||||
?.map((i) => HotVideoItemModel.fromJson(i));
|
||||
final list = RecommendFilter.applyFilterToRelatedVideos
|
||||
? items.where((i) => !RecommendFilter.filterAll(i)).toList()
|
||||
: items.toList();
|
||||
? items?.where((i) => !RecommendFilter.filterAll(i)).toList()
|
||||
: items?.toList();
|
||||
return LoadingState.success(list);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
@@ -1046,7 +1048,8 @@ class VideoHttp {
|
||||
}
|
||||
|
||||
// 视频排行
|
||||
static Future<LoadingState> getRankVideoList(int rid) async {
|
||||
static Future<LoadingState<List<HotVideoItemModel>>> getRankVideoList(
|
||||
int rid) async {
|
||||
var rankApi = "${Api.getRankApi}?rid=$rid&type=all";
|
||||
var res = await Request().get(rankApi);
|
||||
if (res.data['code'] == 0) {
|
||||
@@ -1094,7 +1097,7 @@ class VideoHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> noteList({
|
||||
static Future<LoadingState<List<FavArticleModel>?>> noteList({
|
||||
required int page,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
@@ -1106,13 +1109,16 @@ class VideoHttp {
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(res.data['data']?['list']);
|
||||
List<FavArticleModel>? list = (res.data['data']?['list'] as List?)
|
||||
?.map((e) => FavArticleModel.fromJson(e))
|
||||
.toList();
|
||||
return LoadingState.success(list);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> userNoteList({
|
||||
static Future<LoadingState<List<FavArticleModel>?>> userNoteList({
|
||||
required int page,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
@@ -1124,7 +1130,10 @@ class VideoHttp {
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(res.data['data']?['list']);
|
||||
List<FavArticleModel>? list = (res.data['data']?['list'] as List?)
|
||||
?.map((e) => FavArticleModel.fromJson(e))
|
||||
.toList();
|
||||
return LoadingState.success(list);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
|
||||
class BangumiListDataModel {
|
||||
BangumiListDataModel({
|
||||
this.hasNext,
|
||||
@@ -24,7 +27,7 @@ class BangumiListDataModel {
|
||||
}
|
||||
}
|
||||
|
||||
class BangumiListItemModel {
|
||||
class BangumiListItemModel with MultiSelectData {
|
||||
BangumiListItemModel({
|
||||
this.badge,
|
||||
this.badgeType,
|
||||
@@ -66,8 +69,6 @@ class BangumiListItemModel {
|
||||
Map? newEp;
|
||||
String? progress;
|
||||
|
||||
bool? checked;
|
||||
|
||||
BangumiListItemModel.fromJson(Map<String, dynamic> json) {
|
||||
badge = json['badge'] == '' ? null : json['badge'];
|
||||
badgeType = json['badge_type'];
|
||||
|
||||
@@ -23,8 +23,7 @@ class LiveFollowingModel {
|
||||
count = json['count'];
|
||||
list = (json['list'] as List?)
|
||||
?.map((item) => LiveFollowingItemModel.fromJson(item))
|
||||
.toList() ??
|
||||
<LiveFollowingItemModel>[];
|
||||
.toList();
|
||||
liveCount = json['live_count'];
|
||||
neverLivedCount = json['never_lived_count'];
|
||||
neverLivedFaces = json['never_lived_faces'];
|
||||
|
||||
31
lib/models/member/article.dart
Normal file
31
lib/models/member/article.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
|
||||
class FavArticleModel with MultiSelectData {
|
||||
FavArticleModel({
|
||||
this.webUrl,
|
||||
this.title,
|
||||
this.summary,
|
||||
this.message,
|
||||
this.pic,
|
||||
this.cvid,
|
||||
this.noteId,
|
||||
});
|
||||
|
||||
String? webUrl;
|
||||
String? title;
|
||||
String? summary;
|
||||
String? message;
|
||||
String? pic;
|
||||
dynamic cvid;
|
||||
dynamic noteId;
|
||||
|
||||
FavArticleModel.fromJson(Map json) {
|
||||
webUrl = json['web_url'];
|
||||
title = json['title'];
|
||||
summary = json['summary'];
|
||||
message = json['message'];
|
||||
pic = json['arc']?['pic'];
|
||||
cvid = json['cvid'];
|
||||
noteId = json['note_id'];
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
class MemberTagItemModel {
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
|
||||
class MemberTagItemModel with MultiSelectData {
|
||||
MemberTagItemModel({
|
||||
this.count,
|
||||
this.name,
|
||||
this.tagid,
|
||||
this.tip,
|
||||
this.checked,
|
||||
});
|
||||
|
||||
int? count;
|
||||
String? name;
|
||||
int? tagid;
|
||||
String? tip;
|
||||
bool? checked;
|
||||
|
||||
MemberTagItemModel.fromJson(Map<String, dynamic> json) {
|
||||
count = json['count'];
|
||||
name = json['name'];
|
||||
tagid = json['tagid'];
|
||||
tip = json['tip'];
|
||||
checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'model_owner.dart';
|
||||
import 'model_rec_video_item.dart';
|
||||
import 'model_video.dart';
|
||||
|
||||
// 稍后再看, 排行榜等网页返回也使用该类
|
||||
class HotVideoItemModel extends BaseRecVideoItemModel {
|
||||
class HotVideoItemModel extends BaseRecVideoItemModel with MultiSelectData {
|
||||
int? videos;
|
||||
int? tid;
|
||||
String? tname;
|
||||
@@ -16,8 +18,6 @@ class HotVideoItemModel extends BaseRecVideoItemModel {
|
||||
String? pgcLabel;
|
||||
String? redirectUrl;
|
||||
|
||||
bool? checked; // 手动设置的
|
||||
|
||||
num? progress;
|
||||
|
||||
HotVideoItemModel.fromJson(Map<String, dynamic> json) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
|
||||
import '../model_owner.dart';
|
||||
import '../model_video.dart';
|
||||
import 'fav_folder.dart';
|
||||
@@ -18,7 +21,7 @@ class FavDetailData {
|
||||
}
|
||||
}
|
||||
|
||||
class FavDetailItemData extends BaseVideoItemModel {
|
||||
class FavDetailItemData extends BaseVideoItemModel with MultiSelectData {
|
||||
int? id;
|
||||
int? type;
|
||||
int? page;
|
||||
@@ -31,7 +34,6 @@ class FavDetailItemData extends BaseVideoItemModel {
|
||||
int? favTime;
|
||||
Map? ogv;
|
||||
String? epId;
|
||||
bool? checked;
|
||||
|
||||
FavDetailItemData.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
|
||||
class HistoryData {
|
||||
HistoryData({
|
||||
this.cursor,
|
||||
@@ -59,7 +62,7 @@ class HisTabItem {
|
||||
}
|
||||
}
|
||||
|
||||
class HisListItem {
|
||||
class HisListItem with MultiSelectData {
|
||||
late String title;
|
||||
String? longTitle;
|
||||
String? cover;
|
||||
@@ -84,7 +87,6 @@ class HisListItem {
|
||||
int? kid;
|
||||
String? tagName;
|
||||
int? liveStatus;
|
||||
bool? checked;
|
||||
dynamic isFullScreen;
|
||||
|
||||
HisListItem.fromJson(Map<String, dynamic> json) {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/bangumi/list.dart';
|
||||
import 'package:PiliPlus/models/common/tab_type.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/http/bangumi.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
|
||||
class BangumiController extends CommonController {
|
||||
class BangumiController extends CommonListController<
|
||||
List<BangumiListItemModel>?, BangumiListItemModel> {
|
||||
BangumiController({required this.tabType});
|
||||
final TabType tabType;
|
||||
|
||||
@@ -75,7 +76,8 @@ class BangumiController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => BangumiHttp.bangumiList(
|
||||
Future<LoadingState<List<BangumiListItemModel>?>> customGetData() =>
|
||||
BangumiHttp.bangumiList(
|
||||
page: currentPage,
|
||||
indexType: tabType == TabType.cinema ? 102 : null, // TODO: sort
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'dart:convert';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/repost_dyn_panel.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/pay_coins_page.dart';
|
||||
@@ -26,7 +26,8 @@ import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:html/parser.dart' as html_parser;
|
||||
import 'package:html/dom.dart' as dom;
|
||||
|
||||
class BangumiIntroController extends CommonController {
|
||||
class BangumiIntroController
|
||||
extends CommonDataController<BangumiInfoModel, BangumiInfoModel> {
|
||||
// 视频bvid
|
||||
String bvid = Get.parameters['bvid'] ?? '';
|
||||
var seasonId = Get.parameters['seasonId'] != null
|
||||
@@ -125,14 +126,15 @@ class BangumiIntroController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
epId ??= response.response.episodes!.first.id;
|
||||
bool customHandleResponse(
|
||||
bool isRefresh, Success<BangumiInfoModel> response) {
|
||||
epId ??= response.response.episodes?.firstOrNull?.id;
|
||||
loadingState.value = response;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<BangumiInfoModel>> customGetData() =>
|
||||
SearchHttp.bangumiInfoNew(seasonId: seasonId, epId: epId);
|
||||
|
||||
// 获取点赞/投币/收藏状态
|
||||
@@ -147,40 +149,15 @@ class BangumiIntroController extends CommonController {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取点赞状态
|
||||
// Future queryHasLikeVideo() async {
|
||||
// var result = await VideoHttp.hasLikeVideo(bvid: bvid);
|
||||
// // data num 被点赞标志 0:未点赞 1:已点赞
|
||||
// hasLike.value = result["data"] == 1 ? true : false;
|
||||
// }
|
||||
|
||||
// 获取投币状态
|
||||
// Future queryHasCoinVideo() async {
|
||||
// var result = await VideoHttp.hasCoinVideo(bvid: bvid);
|
||||
// hasCoin.value = result["data"]['multiply'] == 0 ? false : true;
|
||||
// }
|
||||
|
||||
// 获取收藏状态
|
||||
// Future queryHasFavVideo() async {
|
||||
// var result = await VideoHttp.hasFavVideo(aid: IdUtils.bv2av(bvid));
|
||||
// if (result['status']) {
|
||||
// hasFav.value = result["data"]['favoured'];
|
||||
// } else {
|
||||
// hasFav.value = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// (取消)点赞
|
||||
Future actionLikeVideo() async {
|
||||
var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value);
|
||||
if (result['status']) {
|
||||
SmartDialog.showToast(!hasLike.value ? result['data']['toast'] : '取消赞');
|
||||
hasLike.value = !hasLike.value;
|
||||
dynamic bangumiDetail = (loadingState.value as Success).response;
|
||||
BangumiInfoModel bangumiDetail = (loadingState.value as Success).response;
|
||||
bangumiDetail.stat!['likes'] =
|
||||
bangumiDetail.stat!['likes'] + (!hasLike.value ? 1 : -1);
|
||||
loadingState.value = LoadingState.success(bangumiDetail);
|
||||
hasLike.refresh();
|
||||
hasLike.value = !hasLike.value;
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
@@ -194,14 +171,13 @@ class BangumiIntroController extends CommonController {
|
||||
);
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast('投币成功');
|
||||
hasCoin.value = true;
|
||||
dynamic bangumiDetail = (loadingState.value as Success).response;
|
||||
BangumiInfoModel bangumiDetail = (loadingState.value as Success).response;
|
||||
bangumiDetail.stat!['coins'] = bangumiDetail.stat!['coins'] + coin;
|
||||
if (selectLike && hasLike.value.not) {
|
||||
hasLike.value = true;
|
||||
bangumiDetail.stat!['likes'] = bangumiDetail.stat!['likes'] + 1;
|
||||
}
|
||||
loadingState.value = LoadingState.success(bangumiDetail);
|
||||
hasCoin.value = true;
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
@@ -236,60 +212,6 @@ class BangumiIntroController extends CommonController {
|
||||
},
|
||||
),
|
||||
);
|
||||
// showDialog(
|
||||
// context: Get.context!,
|
||||
// builder: (context) {
|
||||
// return AlertDialog(
|
||||
// title: const Text('选择投币个数'),
|
||||
// contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12),
|
||||
// content: StatefulBuilder(builder: (context, StateSetter setState) {
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// RadioListTile(
|
||||
// value: 1,
|
||||
// title: const Text('1枚'),
|
||||
// groupValue: _tempThemeValue,
|
||||
// onChanged: (value) {
|
||||
// _tempThemeValue = value!;
|
||||
// Get.appUpdate();
|
||||
// },
|
||||
// ),
|
||||
// RadioListTile(
|
||||
// value: 2,
|
||||
// title: const Text('2枚'),
|
||||
// groupValue: _tempThemeValue,
|
||||
// onChanged: (value) {
|
||||
// _tempThemeValue = value!;
|
||||
// Get.appUpdate();
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }),
|
||||
// actions: [
|
||||
// TextButton(onPressed: () => Get.back(), child: const Text('取消')),
|
||||
// TextButton(
|
||||
// onPressed: () async {
|
||||
// var res = await VideoHttp.coinVideo(
|
||||
// bvid: bvid, multiply: _tempThemeValue);
|
||||
// if (res['status']) {
|
||||
// SmartDialog.showToast('投币成功');
|
||||
// hasCoin.value = true;
|
||||
// dynamic bangumiDetail =
|
||||
// (loadingState.value as Success).response;
|
||||
// bangumiDetail.stat!['coins'] =
|
||||
// bangumiDetail.stat!['coins'] + _tempThemeValue;
|
||||
// loadingState.value = LoadingState.success(bangumiDetail);
|
||||
// } else {
|
||||
// SmartDialog.showToast(res['msg']);
|
||||
// }
|
||||
// Get.back();
|
||||
// },
|
||||
// child: const Text('确定'))
|
||||
// ],
|
||||
// );
|
||||
// });
|
||||
}
|
||||
|
||||
// (取消)收藏 bangumi
|
||||
@@ -582,8 +504,11 @@ class BangumiIntroController extends CommonController {
|
||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||
PlayRepeat playRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||
|
||||
if ((loadingState.value as Success).response.episodes != null) {
|
||||
episodes = (loadingState.value as Success).response.episodes!;
|
||||
if ((loadingState.value as Success<BangumiInfoModel>).response.episodes !=
|
||||
null) {
|
||||
episodes = (loadingState.value as Success<BangumiInfoModel>)
|
||||
.response
|
||||
.episodes!;
|
||||
} else {
|
||||
if (playRepeat == PlayRepeat.autoPlayRelated) {
|
||||
return playRelated();
|
||||
|
||||
@@ -131,8 +131,7 @@ class BangumiInfo extends StatefulWidget {
|
||||
State<BangumiInfo> createState() => _BangumiInfoState();
|
||||
}
|
||||
|
||||
class _BangumiInfoState extends State<BangumiInfo>
|
||||
with TickerProviderStateMixin {
|
||||
class _BangumiInfoState extends State<BangumiInfo> {
|
||||
late final BangumiIntroController bangumiIntroController;
|
||||
late final VideoDetailController videoDetailCtr;
|
||||
late final BangumiInfoModel? bangumiItem;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'package:PiliPlus/http/bangumi.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/bangumi/pgc_index/condition.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:get/get.dart' hide Condition;
|
||||
|
||||
class PgcIndexController extends CommonController {
|
||||
class PgcIndexController extends CommonListController {
|
||||
PgcIndexController(this.indexType);
|
||||
int? indexType;
|
||||
Rx<LoadingState> conditionState = LoadingState.loading().obs;
|
||||
@@ -51,20 +50,16 @@ class PgcIndexController extends CommonController {
|
||||
);
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
List? getDataList(response) {
|
||||
return response['list'];
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
if (response.response['has_next'] == null ||
|
||||
response.response['has_next'] == 0) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (isEnd.not && (response.response['list'] as List?).isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
response.response['list'] ??= [];
|
||||
response.response['list']!
|
||||
.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(response.response['list']);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,10 +207,10 @@ class _PgcIndexPageState extends State<PgcIndexPage>
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildList(LoadingState loadingState) {
|
||||
Widget _buildList(LoadingState<List<dynamic>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => HttpError(errMsg: '加载中'),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
@@ -221,13 +221,13 @@ class _PgcIndexPageState extends State<PgcIndexPage>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_ctr.onLoadMore();
|
||||
}
|
||||
return BangumiCardVPgcIndex(
|
||||
bangumiItem: loadingState.response[index]);
|
||||
bangumiItem: loadingState.response![index]);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
)
|
||||
: HttpError(callback: _ctr.onReload),
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/bangumi/list.dart';
|
||||
import 'package:PiliPlus/models/common/tab_type.dart';
|
||||
import 'package:PiliPlus/pages/bangumi/pgc_index/pgc_index_page.dart';
|
||||
import 'package:PiliPlus/pages/common/common_page.dart';
|
||||
@@ -230,10 +231,10 @@ class _BangumiPageState extends CommonPageState<BangumiPage, BangumiController>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<BangumiListItemModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => const SliverToBoxAdapter(),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
// 行间距
|
||||
@@ -247,13 +248,13 @@ class _BangumiPageState extends CommonPageState<BangumiPage, BangumiController>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
return BangumiCardV(
|
||||
bangumiItem: loadingState.response[index]);
|
||||
bangumiItem: loadingState.response![index]);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
)
|
||||
: HttpError(
|
||||
|
||||
61
lib/pages/blacklist/controller.dart
Normal file
61
lib/pages/blacklist/controller.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'package:PiliPlus/common/widgets/dialog.dart';
|
||||
import 'package:PiliPlus/http/black.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/user/black.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class BlackListController
|
||||
extends CommonListController<BlackListDataModel, BlackListItem> {
|
||||
int pageSize = 50;
|
||||
RxInt total = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
List<BlackListItem>? getDataList(BlackListDataModel response) {
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (length >= total.value) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(
|
||||
bool isRefresh, Success<BlackListDataModel> response) {
|
||||
total.value = response.response.total ?? 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
Future onRemove(BuildContext context, int index, name, mid) async {
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
title: '确定将 $name 移出黑名单?',
|
||||
onConfirm: () async {
|
||||
var result = await VideoHttp.relationMod(mid: mid, act: 6, reSrc: 11);
|
||||
if (result['status']) {
|
||||
List<BlackListItem> list = (loadingState.value as Success).response;
|
||||
list.removeAt(index);
|
||||
total.value -= 1;
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('操作成功');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<BlackListDataModel>> customGetData() =>
|
||||
BlackHttp.blackList(pn: currentPage, ps: pageSize);
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
import 'package:PiliPlus/common/widgets/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/user/black.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/pages/blacklist/controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/black.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
|
||||
@@ -50,36 +45,37 @@ class _BlackListPageState extends State<BlackListPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<BlackListItem>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? ListView.builder(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _blackListController.scrollController,
|
||||
itemCount: loadingState.response.length,
|
||||
itemCount: loadingState.response!.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_blackListController.onLoadMore();
|
||||
}
|
||||
final item = loadingState.response![index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
'/member?mid=${loadingState.response[index].mid}');
|
||||
Get.toNamed('/member?mid=${item.mid}');
|
||||
},
|
||||
leading: NetworkImgLayer(
|
||||
width: 45,
|
||||
height: 45,
|
||||
type: 'avatar',
|
||||
src: loadingState.response[index].face,
|
||||
src: item.face,
|
||||
),
|
||||
title: Text(
|
||||
loadingState.response[index].uname!,
|
||||
item.uname!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
subtitle: Text(
|
||||
Utils.dateFormat(loadingState.response[index].mtime),
|
||||
Utils.dateFormat(item.mtime),
|
||||
maxLines: 1,
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
@@ -87,10 +83,11 @@ class _BlackListPageState extends State<BlackListPage> {
|
||||
),
|
||||
dense: true,
|
||||
trailing: TextButton(
|
||||
onPressed: () => _blackListController.removeBlack(
|
||||
onPressed: () => _blackListController.onRemove(
|
||||
context,
|
||||
loadingState.response[index].uname,
|
||||
loadingState.response[index].mid,
|
||||
index,
|
||||
item.uname,
|
||||
item.mid,
|
||||
),
|
||||
child: const Text('移除'),
|
||||
),
|
||||
@@ -108,56 +105,3 @@ class _BlackListPageState extends State<BlackListPage> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class BlackListController extends CommonController {
|
||||
int pageSize = 50;
|
||||
RxInt total = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
total.value = response.response.total;
|
||||
if ((response.response.list as List?).isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
response.response.list ??= <BlackListItem>[];
|
||||
response.response.list!
|
||||
.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
if (isEnd.not && response.response.list.length >= total.value) {
|
||||
isEnd = true;
|
||||
}
|
||||
loadingState.value = LoadingState.success(response.response.list.isNotEmpty
|
||||
? response.response.list
|
||||
: <BlackListItem>[]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future removeBlack(context, name, mid) async {
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
title: '确定将 $name 移出黑名单?',
|
||||
onConfirm: () async {
|
||||
var result = await VideoHttp.relationMod(mid: mid, act: 6, reSrc: 11);
|
||||
if (result['status']) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
list.removeWhere((e) => e.mid == mid);
|
||||
total.value = total.value - 1;
|
||||
loadingState.value =
|
||||
LoadingState.success(list.isNotEmpty ? list : <BlackListItem>[]);
|
||||
SmartDialog.showToast('操作成功');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
BlackHttp.blackList(pn: currentPage, ps: pageSize);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ abstract mixin class ScrollOrRefreshMixin {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CommonController extends GetxController
|
||||
abstract class CommonController<R, T> extends GetxController
|
||||
with ScrollOrRefreshMixin {
|
||||
@override
|
||||
final ScrollController scrollController = ScrollController();
|
||||
@@ -35,13 +35,13 @@ abstract class CommonController extends GetxController
|
||||
late int currentPage = 1;
|
||||
bool isLoading = false;
|
||||
late bool isEnd = false;
|
||||
Rx<LoadingState> loadingState = LoadingState.loading().obs;
|
||||
Rx<LoadingState> get loadingState;
|
||||
|
||||
Future<LoadingState> customGetData();
|
||||
Future<LoadingState<R>> customGetData();
|
||||
|
||||
void handleListResponse(List dataList) {}
|
||||
|
||||
bool customHandleResponse(Success response) {
|
||||
bool customHandleResponse(bool isRefresh, Success<R> response) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -49,27 +49,36 @@ abstract class CommonController extends GetxController
|
||||
return false;
|
||||
}
|
||||
|
||||
List<T>? getDataList(R response) {
|
||||
return response as List<T>?;
|
||||
}
|
||||
|
||||
void checkIsEnd(int length) {}
|
||||
|
||||
Future queryData([bool isRefresh = true]) async {
|
||||
if (isLoading || (isRefresh.not && isEnd)) return;
|
||||
isLoading = true;
|
||||
LoadingState response = await customGetData();
|
||||
if (response is Success) {
|
||||
if (!customHandleResponse(response)) {
|
||||
List? dataList = response.response;
|
||||
LoadingState<R> response = await customGetData();
|
||||
if (response is Success<R>) {
|
||||
if (!customHandleResponse(isRefresh, response)) {
|
||||
List<T>? dataList = getDataList(response.response);
|
||||
if (dataList.isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
if (isRefresh) {
|
||||
loadingState.value = response;
|
||||
loadingState.value = LoadingState<List<T>?>.success(dataList);
|
||||
}
|
||||
isLoading = false;
|
||||
return;
|
||||
}
|
||||
handleListResponse(dataList!);
|
||||
if (isRefresh) {
|
||||
loadingState.value = LoadingState.success(dataList);
|
||||
checkIsEnd(dataList.length);
|
||||
loadingState.value = LoadingState<List<T>?>.success(dataList);
|
||||
} else if (loadingState.value is Success) {
|
||||
List currentList = (loadingState.value as Success).response ?? [];
|
||||
currentList.addAll(dataList);
|
||||
loadingState.value = LoadingState.success(currentList);
|
||||
List<T> list = (loadingState.value as Success).response;
|
||||
list.addAll(dataList);
|
||||
checkIsEnd(list.length);
|
||||
loadingState.refresh();
|
||||
}
|
||||
}
|
||||
currentPage++;
|
||||
|
||||
8
lib/pages/common/common_data_controller.dart
Normal file
8
lib/pages/common/common_data_controller.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
abstract class CommonDataController<R, T> extends CommonController<R, T> {
|
||||
@override
|
||||
Rx<LoadingState<T>> loadingState = LoadingState<T>.loading().obs;
|
||||
}
|
||||
9
lib/pages/common/common_list_controller.dart
Normal file
9
lib/pages/common/common_list_controller.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
abstract class CommonListController<R, T> extends CommonController<R, T> {
|
||||
@override
|
||||
Rx<LoadingState<List<T>?>> loadingState =
|
||||
LoadingState<List<T>?>.loading().obs;
|
||||
}
|
||||
@@ -1,32 +1,43 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
|
||||
abstract class MultiSelectController extends CommonController {
|
||||
mixin class MultiSelectData {
|
||||
bool? checked;
|
||||
}
|
||||
|
||||
abstract class MultiSelectController<R, T extends MultiSelectData>
|
||||
extends CommonListController<R, T> {
|
||||
late final RxBool enableMultiSelect = false.obs;
|
||||
late final RxInt checkedCount = 0.obs;
|
||||
late final allSelected = false.obs;
|
||||
|
||||
onSelect(int index) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
list[index].checked = !(list[index]?.checked ?? false);
|
||||
void onSelect(int index, [bool disableSelect = true]) {
|
||||
List<T> list = (loadingState.value as Success).response;
|
||||
list[index].checked = !(list[index].checked ?? false);
|
||||
checkedCount.value = list.where((item) => item.checked == true).length;
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
if (disableSelect) {
|
||||
if (checkedCount.value == 0) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
} else {
|
||||
allSelected.value = checkedCount.value == list.length;
|
||||
}
|
||||
}
|
||||
|
||||
void handleSelect([bool checked = false]) {
|
||||
void handleSelect([bool checked = false, bool disableSelect = true]) {
|
||||
if (loadingState.value is Success) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
if (list.isNotEmpty) {
|
||||
loadingState.value = LoadingState.success(
|
||||
list.map((item) => item..checked = checked).toList());
|
||||
List<T>? list = (loadingState.value as Success).response;
|
||||
if (list?.isNotEmpty == true) {
|
||||
for (T item in list!) {
|
||||
item.checked = checked;
|
||||
}
|
||||
loadingState.refresh();
|
||||
checkedCount.value = checked ? list.length : 0;
|
||||
}
|
||||
}
|
||||
if (checked.not) {
|
||||
if (disableSelect && !checked) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ 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';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/reply_new/reply_page.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
@@ -16,12 +16,11 @@ 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';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:get/get_navigation/src/dialog/dialog_route.dart';
|
||||
|
||||
abstract class ReplyController extends CommonController {
|
||||
abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
|
||||
String nextOffset = '';
|
||||
RxInt count = (-1).obs;
|
||||
|
||||
@@ -31,6 +30,7 @@ abstract class ReplyController extends CommonController {
|
||||
|
||||
late final bool isLogin = Accounts.main.isLogin;
|
||||
|
||||
dynamic upMid;
|
||||
CursorReply? cursor;
|
||||
late Rx<Mode> mode = Mode.MAIN_LIST_HOT.obs;
|
||||
late bool hasUpTop = false;
|
||||
@@ -59,6 +59,29 @@ abstract class ReplyController extends CommonController {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (length >= count.value) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
MainListReply data = response.response;
|
||||
cursor = data.cursor;
|
||||
count.value = data.subjectControl.count.toInt();
|
||||
if (isRefresh) {
|
||||
upMid ??= data.subjectControl.upMid;
|
||||
hasUpTop = data.hasUpTop();
|
||||
if (data.hasUpTop()) {
|
||||
data.replies.insert(0, data.upTop);
|
||||
}
|
||||
}
|
||||
isEnd = data.cursor.isEnd;
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future onRefresh() {
|
||||
cursor = null;
|
||||
@@ -66,29 +89,6 @@ abstract class ReplyController extends CommonController {
|
||||
return super.onRefresh();
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
MainListReply replies = response.response;
|
||||
if (cursor == null) {
|
||||
count.value = replies.subjectControl.count.toInt();
|
||||
hasUpTop = replies.hasUpTop();
|
||||
if (replies.hasUpTop()) {
|
||||
replies.replies.insert(0, replies.upTop);
|
||||
}
|
||||
}
|
||||
cursor = replies.cursor;
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
replies.replies
|
||||
.insertAll(0, (loadingState.value as Success).response.replies);
|
||||
}
|
||||
isEnd = replies.replies.isEmpty ||
|
||||
replies.cursor.isEnd ||
|
||||
replies.replies.length >= count.value;
|
||||
loadingState.value = LoadingState.success(replies);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 排序搜索评论
|
||||
queryBySort() {
|
||||
EasyThrottle.throttle('queryBySort', const Duration(seconds: 1), () {
|
||||
@@ -168,16 +168,24 @@ abstract class ReplyController extends CommonController {
|
||||
if (res != null) {
|
||||
savedReplies[key] = null;
|
||||
ReplyInfo replyInfo = Utils.replyCast(res);
|
||||
MainListReply response = loadingState.value is Success
|
||||
? (loadingState.value as Success).response
|
||||
: MainListReply();
|
||||
if (oid != null) {
|
||||
response.replies.insert(hasUpTop ? 1 : 0, replyInfo);
|
||||
if (loadingState.value is Success) {
|
||||
List<ReplyInfo>? list = (loadingState.value as Success).response;
|
||||
if (list == null) {
|
||||
loadingState.value = LoadingState.success([replyInfo]);
|
||||
} else {
|
||||
response.replies[index].replies.add(replyInfo);
|
||||
if (oid != null) {
|
||||
list.insert(hasUpTop ? 1 : 0, replyInfo);
|
||||
} else {
|
||||
list[index].replies.add(replyInfo);
|
||||
}
|
||||
loadingState.refresh();
|
||||
}
|
||||
} else {
|
||||
loadingState.value = LoadingState.success([replyInfo]);
|
||||
}
|
||||
count.value += 1;
|
||||
loadingState.value = LoadingState.success(response);
|
||||
|
||||
// check reply
|
||||
if (enableCommAntifraud && context.mounted) {
|
||||
checkReply(
|
||||
context: context,
|
||||
@@ -203,28 +211,19 @@ abstract class ReplyController extends CommonController {
|
||||
);
|
||||
}
|
||||
|
||||
onMDelete(rpid, frpid) {
|
||||
MainListReply response = (loadingState.value as Success).response;
|
||||
if (frpid == null) {
|
||||
response.replies.removeWhere((item) {
|
||||
return item.id.toInt() == rpid;
|
||||
});
|
||||
void onRemove(int index, int? subIndex) {
|
||||
List<ReplyInfo> list = (loadingState.value as Success).response;
|
||||
if (subIndex == null) {
|
||||
list.removeAt(index);
|
||||
} else {
|
||||
response.replies.map((item) {
|
||||
if (item.id == frpid) {
|
||||
return item..replies.removeWhere((reply) => reply.id.toInt() == rpid);
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}).toList();
|
||||
list[index].replies.removeAt(subIndex);
|
||||
}
|
||||
count.value -= 1;
|
||||
loadingState.value = LoadingState.success(response);
|
||||
loadingState.refresh();
|
||||
}
|
||||
|
||||
void onCheckReply(context, item) {
|
||||
try {
|
||||
if (item is ReplyInfo) {
|
||||
checkReply(
|
||||
context: context,
|
||||
oid: item.oid.toInt(),
|
||||
@@ -242,24 +241,6 @@ abstract class ReplyController extends CommonController {
|
||||
//
|
||||
isManual: true,
|
||||
);
|
||||
} else if (item is ReplyItemModel) {
|
||||
checkReply(
|
||||
context: context,
|
||||
oid: item.oid,
|
||||
rpid: item.root == 0 ? null : item.root,
|
||||
replyType: item.type!,
|
||||
replyId: item.rpid!,
|
||||
message: item.content!.message!,
|
||||
//
|
||||
root: item.root,
|
||||
parent: item.parent,
|
||||
ctime: item.ctime,
|
||||
pictures: item.content?.pictures,
|
||||
mid: item.mid,
|
||||
//
|
||||
isManual: true,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
@@ -502,16 +483,14 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
|
||||
isUpTop: isUpTop,
|
||||
);
|
||||
if (res['status']) {
|
||||
final data = (loadingState.value as Success).response;
|
||||
if (data is MainListReply) {
|
||||
data.replies[index].replyControl.isUpTop = !isUpTop;
|
||||
List<ReplyInfo> list = (loadingState.value as Success).response;
|
||||
list[index].replyControl.isUpTop = !isUpTop;
|
||||
if (!isUpTop && index != 0) {
|
||||
data.replies[0].replyControl.isUpTop = false;
|
||||
final item = data.replies.removeAt(index);
|
||||
data.replies.insert(0, item);
|
||||
}
|
||||
loadingState.value = LoadingState.success(data);
|
||||
list[0].replyControl.isUpTop = false;
|
||||
final item = list.removeAt(index);
|
||||
list.insert(0, item);
|
||||
}
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('${isUpTop ? '取消' : ''}置顶成功');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'package:PiliPlus/http/html.dart';
|
||||
import 'package:PiliPlus/http/reply.dart';
|
||||
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
|
||||
class DynamicDetailController extends ReplyController {
|
||||
class DynamicDetailController extends ReplyController<MainListReply> {
|
||||
DynamicDetailController(this.oid, this.type);
|
||||
int oid;
|
||||
int type;
|
||||
@@ -47,7 +47,13 @@ class DynamicDetailController extends ReplyController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => ReplyHttp.replyListGrpc(
|
||||
List<ReplyInfo>? getDataList(MainListReply response) {
|
||||
return response.replies;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<MainListReply>> customGetData() =>
|
||||
ReplyHttp.replyListGrpc(
|
||||
type: type,
|
||||
oid: oid,
|
||||
cursor: CursorReq(
|
||||
|
||||
@@ -775,7 +775,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget replyList(LoadingState loadingState) {
|
||||
Widget replyList(LoadingState<List<ReplyInfo>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
@@ -785,11 +785,11 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
childCount: 8,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response.replies as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.replies.length) {
|
||||
if (index == loadingState.response!.length) {
|
||||
_dynamicDetailController.onLoadMore();
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
@@ -799,7 +799,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
child: Text(
|
||||
_dynamicDetailController.isEnd.not
|
||||
? '加载中...'
|
||||
: loadingState.response.replies.isEmpty
|
||||
: loadingState.response!.isEmpty
|
||||
? '还没有评论'
|
||||
: '没有更多了',
|
||||
style: TextStyle(
|
||||
@@ -810,19 +810,20 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
);
|
||||
} else {
|
||||
return ReplyItemGrpc(
|
||||
replyItem: loadingState.response.replies[index],
|
||||
replyItem: loadingState.response![index],
|
||||
replyLevel: '1',
|
||||
replyReply: (replyItem, id) =>
|
||||
replyReply(context, replyItem, id),
|
||||
onReply: () {
|
||||
_dynamicDetailController.onReply(
|
||||
context,
|
||||
replyItem: loadingState.response.replies[index],
|
||||
replyItem: loadingState.response![index],
|
||||
index: index,
|
||||
);
|
||||
},
|
||||
onDelete: _dynamicDetailController.onMDelete,
|
||||
upMid: loadingState.response.subjectControl.upMid,
|
||||
onDelete: (subIndex) =>
|
||||
_dynamicDetailController.onRemove(index, subIndex),
|
||||
upMid: _dynamicDetailController.upMid,
|
||||
callback: _getImageCallback,
|
||||
onCheckReply: (item) =>
|
||||
_dynamicDetailController.onCheckReply(context, item),
|
||||
@@ -837,7 +838,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
);
|
||||
}
|
||||
},
|
||||
childCount: loadingState.response.replies.length + 1,
|
||||
childCount: loadingState.response!.length + 1,
|
||||
),
|
||||
)
|
||||
: HttpError(
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/main/controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../http/dynamics.dart';
|
||||
|
||||
class DynamicsTabController extends CommonController {
|
||||
class DynamicsTabController
|
||||
extends CommonListController<DynamicsDataModel, DynamicItemModel> {
|
||||
DynamicsTabController({required this.dynamicsType});
|
||||
final String dynamicsType;
|
||||
String offset = '';
|
||||
@@ -32,22 +32,20 @@ class DynamicsTabController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
offset = response.response.offset;
|
||||
if ((response.response.items as List?).isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
response.response.items ??= <DynamicItemModel>[];
|
||||
response.response.items!
|
||||
.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(response.response.items);
|
||||
return true;
|
||||
List<DynamicItemModel>? getDataList(DynamicsDataModel response) {
|
||||
return response.items;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => DynamicsHttp.followDynamic(
|
||||
bool customHandleResponse(
|
||||
bool isRefresh, Success<DynamicsDataModel> response) {
|
||||
offset = response.response.offset ?? '';
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<DynamicsDataModel>> customGetData() =>
|
||||
DynamicsHttp.followDynamic(
|
||||
type: dynamicsType == "up" ? "all" : dynamicsType,
|
||||
offset: offset,
|
||||
mid: dynamicsType == "up" ? mid : -1,
|
||||
@@ -56,9 +54,9 @@ class DynamicsTabController extends CommonController {
|
||||
Future onRemove(dynamic dynamicId) async {
|
||||
var res = await MsgHttp.removeDynamic(dynamicId);
|
||||
if (res['status']) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
List<DynamicItemModel> list = (loadingState.value as Success).response;
|
||||
list.removeWhere((item) => item.idStr == dynamicId);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('删除成功');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/common/common_page.dart';
|
||||
import 'package:PiliPlus/pages/main/controller.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
@@ -137,10 +138,10 @@ class _DynamicsTabPageState
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<DynamicItemModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => skeleton(),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
@@ -153,23 +154,23 @@ class _DynamicsTabPageState
|
||||
// mainAxisSpacing: StyleString.cardSpace / 2,
|
||||
|
||||
lastChildLayoutTypeBuilder: (index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
return index == loadingState.response.length
|
||||
return index == loadingState.response!.length
|
||||
? LastChildLayoutType.foot
|
||||
: LastChildLayoutType.none;
|
||||
},
|
||||
children: [
|
||||
if (dynamicsController.tabController.index == 4 &&
|
||||
dynamicsController.mid.value != -1) ...[
|
||||
for (var i in loadingState.response)
|
||||
for (var i in loadingState.response!)
|
||||
DynamicPanel(
|
||||
item: i,
|
||||
onRemove: controller.onRemove,
|
||||
),
|
||||
] else ...[
|
||||
for (var i in loadingState.response)
|
||||
for (var i in loadingState.response!)
|
||||
if (!dynamicsController.tempBannedList
|
||||
.contains(i.modules?.moduleAuthor?.mid))
|
||||
DynamicPanel(
|
||||
@@ -187,23 +188,24 @@ class _DynamicsTabPageState
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index ==
|
||||
loadingState.response!.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
final item = loadingState.response![index];
|
||||
if ((dynamicsController.tabController.index ==
|
||||
4 &&
|
||||
dynamicsController.mid.value != -1) ||
|
||||
!dynamicsController.tempBannedList.contains(
|
||||
loadingState.response[index].modules
|
||||
?.moduleAuthor?.mid)) {
|
||||
item.modules?.moduleAuthor?.mid)) {
|
||||
return DynamicPanel(
|
||||
item: loadingState.response[index],
|
||||
item: item,
|
||||
onRemove: controller.onRemove,
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v2/dynamic.pb.dart' as dyn;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
|
||||
import '../../../http/constants.dart';
|
||||
import '../controller.dart';
|
||||
|
||||
class AuthorPanelGrpc extends StatelessWidget {
|
||||
final dyn.DynamicItem item;
|
||||
final Function? addBannedList;
|
||||
final String? source;
|
||||
final Function? onRemove;
|
||||
const AuthorPanelGrpc({
|
||||
super.key,
|
||||
required this.item,
|
||||
this.addBannedList,
|
||||
this.source,
|
||||
this.onRemove,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String heroTag = Utils.makeHeroTag(item.modules.first.moduleAuthor.mid);
|
||||
return Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// 番剧
|
||||
// if (item.modules.first.moduleAuthor.type == 'AUTHOR_TYPE_PGC') {
|
||||
// return;
|
||||
// }
|
||||
feedBack();
|
||||
Get.toNamed(
|
||||
'/member?mid=${item.modules.first.moduleAuthor.author.mid}',
|
||||
arguments: {
|
||||
'face': item.modules.first.moduleAuthor.author.face,
|
||||
'heroTag': heroTag
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Hero(
|
||||
tag: heroTag,
|
||||
child: NetworkImgLayer(
|
||||
width: 40,
|
||||
height: 40,
|
||||
type: 'avatar',
|
||||
src: item.modules.first.moduleAuthor.author.face,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
item.modules.first.moduleAuthor.author.name,
|
||||
// semanticsLabel: "UP主:${item.modules.moduleAuthor.name}",
|
||||
style: TextStyle(
|
||||
color: item.modules.first.moduleAuthor.author.vip.status >
|
||||
0 &&
|
||||
item.modules.first.moduleAuthor.author.vip.type == 2
|
||||
? context.vipColor
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// DefaultTextStyle.merge(
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.outline,
|
||||
// fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
// ),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Text(item.modules.first.moduleAuthor.pubTime),
|
||||
// if (item.modules.first.moduleAuthor.pubTime != '' &&
|
||||
// item.modules.first.moduleAuthor.pubAction != '')
|
||||
// const Text(' '),
|
||||
// Text(item.modules.first.moduleAuthor.pubAction),
|
||||
// ],
|
||||
// ),
|
||||
// )
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
// if (source != 'detail' && item.modules.first?.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.first.moduleTag.text,
|
||||
// style: TextStyle(
|
||||
// height: 1,
|
||||
// fontSize: 12,
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// ),
|
||||
// strutStyle: const StrutStyle(
|
||||
// leading: 0,
|
||||
// height: 1,
|
||||
// fontSize: 12,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: IconButton(
|
||||
tooltip: '更多',
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
isScrollControlled: true,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: min(640, min(Get.width, Get.height)),
|
||||
),
|
||||
builder: (context) {
|
||||
return MorePanel(
|
||||
item: item,
|
||||
onRemove: onRemove,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.more_vert_outlined, size: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MorePanel extends StatelessWidget {
|
||||
final dynamic item;
|
||||
final Function? onRemove;
|
||||
const MorePanel({
|
||||
super.key,
|
||||
required this.item,
|
||||
this.onRemove,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: Get.back,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(28),
|
||||
topRight: Radius.circular(28),
|
||||
),
|
||||
child: Container(
|
||||
height: 35,
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 3,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(3))),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (item.type == 'DYNAMIC_TYPE_AV')
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
try {
|
||||
String bvid = item.modules.moduleDynamic.major.archive.bvid;
|
||||
var res = await UserHttp.toViewLater(bvid: bvid);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
Get.back();
|
||||
} catch (err) {
|
||||
SmartDialog.showToast('出错了:${err.toString()}');
|
||||
}
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
// dense: true,
|
||||
leading: const Icon(Icons.watch_later_outlined, size: 19),
|
||||
title: Text(
|
||||
'稍后再看',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'分享动态',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
leading: const Icon(Icons.share_outlined, size: 19),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.shareText(
|
||||
'${HttpString.dynamicShareBaseUrl}/${item.idStr}');
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'临时屏蔽:${item.modules.moduleAuthor.name}',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
leading: const Icon(Icons.visibility_off_outlined, size: 19),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
DynamicsController dynamicsController =
|
||||
Get.find<DynamicsController>();
|
||||
dynamicsController.tempBannedList
|
||||
.add(item.modules.moduleAuthor.mid);
|
||||
SmartDialog.showToast(
|
||||
'已临时屏蔽${item.modules.moduleAuthor.name}(${item.modules.moduleAuthor.mid}),重启恢复');
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
if (item.modules.moduleAuthor.mid == Accounts.main.mid)
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
Get.back();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('确定删除该动态?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
onRemove?.call(item.idStr);
|
||||
},
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
));
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: Icon(Icons.delete_outline,
|
||||
color: Theme.of(context).colorScheme.error, size: 19),
|
||||
title: Text('删除',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(color: Theme.of(context).colorScheme.error)),
|
||||
),
|
||||
const Divider(thickness: 0.1, height: 1),
|
||||
ListTile(
|
||||
onTap: () => Get.back(),
|
||||
minLeadingWidth: 0,
|
||||
dense: true,
|
||||
title: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// 内容
|
||||
import 'package:PiliPlus/common/widgets/image_view.dart';
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v2/dynamic.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'rich_node_panel.dart';
|
||||
|
||||
class ContentGrpc extends StatelessWidget {
|
||||
final DynamicItem item;
|
||||
final String? source;
|
||||
|
||||
const ContentGrpc({
|
||||
super.key,
|
||||
required this.item,
|
||||
this.source,
|
||||
});
|
||||
|
||||
InlineSpan picsNodes() {
|
||||
return WidgetSpan(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) => imageView(
|
||||
constraints.maxWidth,
|
||||
item.modules.first.moduleDynamic.dynDraw.items
|
||||
.map(
|
||||
(item) => ImageModel(
|
||||
width: item.width,
|
||||
height: item.height,
|
||||
url: item.src,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextStyle authorStyle =
|
||||
TextStyle(color: Theme.of(context).colorScheme.primary);
|
||||
InlineSpan? richNodes = richNode(item, context);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 12, 6),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (item.modules.first.moduleDynamic.hasDynTopicSet()) ...[
|
||||
GestureDetector(
|
||||
child: Text(
|
||||
'#${item.modules.first.moduleDynamic.dynTopicSet.topics.first.topicName}',
|
||||
style: authorStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (richNodes != null)
|
||||
IgnorePointer(
|
||||
// 禁用SelectableRegion的触摸交互功能
|
||||
ignoring: source == 'detail' ? false : true,
|
||||
child: SelectableRegion(
|
||||
magnifierConfiguration: const TextMagnifierConfiguration(),
|
||||
focusNode: FocusNode(),
|
||||
selectionControls: MaterialTextSelectionControls(),
|
||||
child: Text.rich(
|
||||
/// fix 默认20px高度
|
||||
style: const TextStyle(height: 0),
|
||||
richNodes,
|
||||
maxLines: source == 'detail' ? null : 6,
|
||||
overflow: source == 'detail' ? null : TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (item.modules.first.moduleDynamic.hasDynDraw())
|
||||
Text.rich(
|
||||
picsNodes(),
|
||||
// semanticsLabel: '动态图片',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v2/dynamic.pb.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/author_panel_grpc.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/content_panel_grpc.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DynamicPanelGrpc extends StatelessWidget {
|
||||
final DynamicItem item;
|
||||
final String? source;
|
||||
final Function? onRemove;
|
||||
|
||||
const DynamicPanelGrpc({
|
||||
required this.item,
|
||||
this.source,
|
||||
this.onRemove,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: source == 'detail'
|
||||
? const EdgeInsets.only(bottom: 12)
|
||||
: EdgeInsets.zero,
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border(
|
||||
// bottom: BorderSide(
|
||||
// width: 8,
|
||||
// color: Theme.of(context).dividerColor.withOpacity(0.05),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
child: Material(
|
||||
elevation: 0,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context).cardColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: source == 'detail' && item.itemType == DynamicType.draw
|
||||
? null
|
||||
: () => Utils.pushDynDetail(item, 1),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 6),
|
||||
child: AuthorPanelGrpc(
|
||||
item: item,
|
||||
source: source,
|
||||
onRemove: onRemove,
|
||||
),
|
||||
),
|
||||
ContentGrpc(item: item, source: source),
|
||||
// forWard(item, context, _dynamicsController, source),
|
||||
const SizedBox(height: 2),
|
||||
// if (source == null) ActionPanel(item: item),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/models/video/reply/emote.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../http/reply.dart';
|
||||
|
||||
class EmotePanelController extends CommonController
|
||||
class EmotePanelController
|
||||
extends CommonListController<List<Packages>?, Packages>
|
||||
with GetTickerProviderStateMixin {
|
||||
TabController? tabController;
|
||||
|
||||
@@ -16,15 +18,17 @@ class EmotePanelController extends CommonController
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
bool customHandleResponse(bool isRefresh, Success<List<Packages>?> response) {
|
||||
if (response.response?.isNotEmpty == true) {
|
||||
tabController =
|
||||
TabController(length: response.response.length, vsync: this);
|
||||
TabController(length: response.response!.length, vsync: this);
|
||||
}
|
||||
loadingState.value = response;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<List<Packages>?>> customGetData() =>
|
||||
ReplyHttp.getEmoteList(business: 'reply');
|
||||
|
||||
@override
|
||||
|
||||
@@ -29,16 +29,16 @@ class _EmotePanelState extends State<EmotePanel>
|
||||
return Obx(() => _buildBody(_emotePanelController.loadingState.value));
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<Packages>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: tabBarView(
|
||||
controller: _emotePanelController.tabController,
|
||||
children: (loadingState.response as List<Packages>).map(
|
||||
children: loadingState.response!.map(
|
||||
(e) {
|
||||
int size = e.emote!.first.meta!.size!;
|
||||
int type = e.type!;
|
||||
@@ -100,7 +100,7 @@ class _EmotePanelState extends State<EmotePanel>
|
||||
dividerColor: Colors.transparent,
|
||||
dividerHeight: 0,
|
||||
isScrollable: true,
|
||||
tabs: (loadingState.response as List<Packages>)
|
||||
tabs: loadingState.response!
|
||||
.map(
|
||||
(e) => Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:PiliPlus/http/fan.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/fans/result.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
|
||||
class FansController extends CommonController {
|
||||
class FansController
|
||||
extends CommonListController<FansDataModel, FansItemModel> {
|
||||
int ps = 20;
|
||||
int total = 0;
|
||||
late int? mid;
|
||||
@@ -28,22 +28,12 @@ class FansController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
if ((currentPage == 1 && response.response.total < ps) ||
|
||||
(response.response.list as List?).isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
response.response.list ??= <FansItemModel>[];
|
||||
response.response.list!
|
||||
.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(response.response.list);
|
||||
return true;
|
||||
List<FansItemModel>? getDataList(FansDataModel response) {
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => FanHttp.fans(
|
||||
Future<LoadingState<FansDataModel>> customGetData() => FanHttp.fans(
|
||||
vmid: mid,
|
||||
pn: currentPage,
|
||||
ps: ps,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/fans/result.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -48,23 +49,27 @@ class _FansPageState extends State<FansPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<FansItemModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => HttpError(),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||
mainAxisExtent: 56,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_fansController.onLoadMore();
|
||||
}
|
||||
return fanItem(item: loadingState.response[index]);
|
||||
return fanItem(item: loadingState.response![index]);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
)
|
||||
: HttpError(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class FavArticleController extends CommonController {
|
||||
class FavArticleController extends CommonListController {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@@ -19,7 +19,7 @@ class FavArticleController extends CommonController {
|
||||
if (res['status']) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
list.removeAt(index);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('已取消收藏');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
|
||||
@@ -40,7 +40,7 @@ class _FavArticlePageState extends State<FavArticlePage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<dynamic>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
@@ -55,7 +55,7 @@ class _FavArticlePageState extends State<FavArticlePage>
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
@@ -69,11 +69,11 @@ class _FavArticlePageState extends State<FavArticlePage>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_favArticleController.onLoadMore();
|
||||
}
|
||||
return FavArticleItem(
|
||||
item: loadingState.response[index],
|
||||
item: loadingState.response![index],
|
||||
onDelete: () {
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
@@ -81,13 +81,13 @@ class _FavArticlePageState extends State<FavArticlePage>
|
||||
onConfirm: () {
|
||||
_favArticleController.onRemove(
|
||||
index,
|
||||
loadingState.response[index]['opus_id'],
|
||||
loadingState.response![index]['opus_id'],
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/member/article.dart';
|
||||
import 'package:PiliPlus/pages/fav/note/controller.dart';
|
||||
import 'package:PiliPlus/pages/fav/note/widget/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
@@ -132,7 +133,7 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<FavArticleModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
@@ -147,7 +148,7 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||
@@ -159,18 +160,18 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_favNoteController.onLoadMore();
|
||||
}
|
||||
return FavNoteItem(
|
||||
item: loadingState.response[index],
|
||||
item: loadingState.response![index],
|
||||
ctr: _favNoteController,
|
||||
onSelect: () {
|
||||
_favNoteController.onSelect(index);
|
||||
},
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/member/article.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FavNoteController extends MultiSelectController {
|
||||
class FavNoteController
|
||||
extends MultiSelectController<List<FavArticleModel>?, FavArticleModel> {
|
||||
FavNoteController(this.isPublish);
|
||||
|
||||
final bool isPublish;
|
||||
|
||||
late final allSelected = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@@ -18,48 +17,36 @@ class FavNoteController extends MultiSelectController {
|
||||
}
|
||||
|
||||
@override
|
||||
onSelect(int index) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
list[index]['checked'] = !(list[index]['checked'] ?? false);
|
||||
checkedCount.value = list.where((item) => item['checked'] == true).length;
|
||||
loadingState.value = LoadingState.success(list);
|
||||
allSelected.value = checkedCount.value == list.length;
|
||||
if (checkedCount.value == 0) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
onSelect(int index, [bool disableSelect = true]) {
|
||||
super.onSelect(index, false);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleSelect([bool checked = false]) {
|
||||
void handleSelect([bool checked = false, bool disableSelect = true]) {
|
||||
allSelected.value = checked;
|
||||
if (loadingState.value is Success) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
if (list.isNotEmpty) {
|
||||
loadingState.value = LoadingState.success(
|
||||
list.map((item) => item..['checked'] = checked).toList());
|
||||
checkedCount.value = checked ? list.length : 0;
|
||||
}
|
||||
}
|
||||
super.handleSelect(checked, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() {
|
||||
Future<LoadingState<List<FavArticleModel>?>> customGetData() {
|
||||
return isPublish
|
||||
? VideoHttp.userNoteList(page: currentPage)
|
||||
: VideoHttp.noteList(page: currentPage);
|
||||
}
|
||||
|
||||
void onRemove() async {
|
||||
List dataList = (loadingState.value as Success).response as List;
|
||||
Set removeList = dataList.where((item) => item['checked'] == true).toSet();
|
||||
List<FavArticleModel> dataList = (loadingState.value as Success).response;
|
||||
Set<FavArticleModel> removeList =
|
||||
dataList.where((item) => item.checked == true).toSet();
|
||||
final res = await VideoHttp.delNote(
|
||||
isPublish: isPublish,
|
||||
noteIds: removeList
|
||||
.map((item) => isPublish ? item['cvid'] : item['note_id'])
|
||||
.map((item) => isPublish ? item.cvid : item.noteId)
|
||||
.toList(),
|
||||
);
|
||||
if (res['status']) {
|
||||
List remainList = dataList.toSet().difference(removeList).toList();
|
||||
List<FavArticleModel> remainList =
|
||||
dataList.toSet().difference(removeList).toList();
|
||||
loadingState.value = LoadingState.success(remainList);
|
||||
enableMultiSelect.value = false;
|
||||
SmartDialog.showToast('删除成功');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/member/article.dart';
|
||||
import 'package:PiliPlus/pages/fav/note/controller.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -12,7 +13,7 @@ class FavNoteItem extends StatelessWidget {
|
||||
required this.onSelect,
|
||||
});
|
||||
|
||||
final dynamic item;
|
||||
final FavArticleModel item;
|
||||
final FavNoteController ctr;
|
||||
final VoidCallback onSelect;
|
||||
|
||||
@@ -26,10 +27,12 @@ class FavNoteItem extends StatelessWidget {
|
||||
onSelect();
|
||||
return;
|
||||
}
|
||||
if (item.webUrl?.isNotEmpty == true) {
|
||||
Utils.handleWebview(
|
||||
item['web_url'],
|
||||
item.webUrl!,
|
||||
inApp: true,
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (!ctr.enableMultiSelect.value) {
|
||||
@@ -53,7 +56,7 @@ class FavNoteItem extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
item['title'],
|
||||
item.title ?? '',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
@@ -64,14 +67,14 @@ class FavNoteItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
item['summary'],
|
||||
item.summary ?? '',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
item['message'],
|
||||
item.message ?? '',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
@@ -80,7 +83,7 @@ class FavNoteItem extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (item['arc']?['pic'] != null) ...[
|
||||
if (item.pic?.isNotEmpty == true) ...[
|
||||
const SizedBox(width: 10),
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
@@ -91,7 +94,7 @@ class FavNoteItem extends StatelessWidget {
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: item['arc']?['pic'],
|
||||
src: item.pic,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
),
|
||||
@@ -100,7 +103,7 @@ class FavNoteItem extends StatelessWidget {
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
AnimatedOpacity(
|
||||
opacity: item['checked'] == true ? 1 : 0,
|
||||
opacity: item.checked == true ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
@@ -115,7 +118,7 @@ class FavNoteItem extends StatelessWidget {
|
||||
width: 34,
|
||||
height: 34,
|
||||
child: AnimatedScale(
|
||||
scale: item['checked'] == true ? 1 : 0,
|
||||
scale: item.checked == true ? 1 : 0,
|
||||
duration:
|
||||
const Duration(milliseconds: 250),
|
||||
curve: Curves.easeInOut,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/bangumi/list.dart';
|
||||
import 'package:PiliPlus/pages/fav/pgc/controller.dart';
|
||||
import 'package:PiliPlus/pages/fav/pgc/widget/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
@@ -126,7 +127,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
||||
if (_favPgcController.checkedCount.value !=
|
||||
0) {
|
||||
_favPgcController
|
||||
.onUpdate(item['followStatus']);
|
||||
.onUpdateList(item['followStatus']);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
@@ -156,7 +157,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<BangumiListItemModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
@@ -171,7 +172,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||
@@ -183,11 +184,12 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_favPgcController.onLoadMore();
|
||||
}
|
||||
final item = loadingState.response![index];
|
||||
return FavPgcItem(
|
||||
item: loadingState.response[index],
|
||||
item: item,
|
||||
ctr: _favPgcController,
|
||||
onSelect: () {
|
||||
_favPgcController.onSelect(index);
|
||||
@@ -201,13 +203,13 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
||||
if (followStatus == -1) {
|
||||
_favPgcController.bangumiDel(
|
||||
index,
|
||||
loadingState.response[index].seasonId,
|
||||
item.seasonId,
|
||||
);
|
||||
} else {
|
||||
_favPgcController.bangumiUpdate(
|
||||
_favPgcController.onUpdate(
|
||||
index,
|
||||
followStatus,
|
||||
loadingState.response[index].seasonId,
|
||||
item.seasonId,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -215,7 +217,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
||||
},
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -8,14 +8,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FavPgcController extends MultiSelectController {
|
||||
class FavPgcController
|
||||
extends MultiSelectController<BangumiListDataModel, BangumiListItemModel> {
|
||||
final int type;
|
||||
final int followStatus;
|
||||
|
||||
FavPgcController(this.type, this.followStatus);
|
||||
|
||||
late final allSelected = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@@ -23,34 +22,24 @@ class FavPgcController extends MultiSelectController {
|
||||
}
|
||||
|
||||
@override
|
||||
onSelect(int index) {
|
||||
List<BangumiListItemModel> list = (loadingState.value as Success).response;
|
||||
list[index].checked = !(list[index].checked ?? false);
|
||||
checkedCount.value = list.where((item) => item.checked == true).length;
|
||||
loadingState.value = LoadingState.success(list);
|
||||
allSelected.value = checkedCount.value == list.length;
|
||||
if (checkedCount.value == 0) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
onSelect(int index, [bool disableSelect = true]) {
|
||||
super.onSelect(index, false);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleSelect([bool checked = false]) {
|
||||
void handleSelect([bool checked = false, bool disableSelect = true]) {
|
||||
allSelected.value = checked;
|
||||
if (loadingState.value is Success) {
|
||||
List<BangumiListItemModel> list =
|
||||
(loadingState.value as Success).response;
|
||||
if (list.isNotEmpty) {
|
||||
loadingState.value = LoadingState.success(list
|
||||
.map<BangumiListItemModel>((item) => item..checked = checked)
|
||||
.toList());
|
||||
checkedCount.value = checked ? list.length : 0;
|
||||
}
|
||||
}
|
||||
super.handleSelect(checked, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => BangumiHttp.bangumiFollowList(
|
||||
List<BangumiListItemModel>? getDataList(BangumiListDataModel response) {
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<BangumiListDataModel>> customGetData() =>
|
||||
BangumiHttp.bangumiFollowList(
|
||||
mid: Accounts.main.mid,
|
||||
type: type,
|
||||
followStatus: followStatus,
|
||||
@@ -71,12 +60,12 @@ class FavPgcController extends MultiSelectController {
|
||||
List<BangumiListItemModel> list =
|
||||
(loadingState.value as Success).response;
|
||||
list.removeAt(index);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
}
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
|
||||
Future onUpdate(followStatus) async {
|
||||
Future onUpdateList(followStatus) async {
|
||||
List<BangumiListItemModel> dataList =
|
||||
(loadingState.value as Success).response as List<BangumiListItemModel>;
|
||||
Set<BangumiListItemModel> updateList =
|
||||
@@ -96,7 +85,7 @@ class FavPgcController extends MultiSelectController {
|
||||
List<BangumiListItemModel> list =
|
||||
(ctr.loadingState.value as Success).response;
|
||||
list.insertAll(0, updateList.map((item) => item..checked = null));
|
||||
ctr.loadingState.value = LoadingState.success(list);
|
||||
ctr.loadingState.refresh();
|
||||
ctr.allSelected.value = false;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -106,7 +95,7 @@ class FavPgcController extends MultiSelectController {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
|
||||
Future bangumiUpdate(index, followStatus, seasonId) async {
|
||||
Future onUpdate(index, followStatus, seasonId) async {
|
||||
var result = await VideoHttp.bangumiUpdate(
|
||||
seasonId: [seasonId],
|
||||
status: followStatus,
|
||||
@@ -115,14 +104,14 @@ class FavPgcController extends MultiSelectController {
|
||||
List<BangumiListItemModel> list =
|
||||
(loadingState.value as Success).response;
|
||||
final item = list.removeAt(index);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
try {
|
||||
final ctr = Get.find<FavPgcController>(tag: '$type$followStatus');
|
||||
if (ctr.loadingState.value is Success) {
|
||||
List<BangumiListItemModel> list =
|
||||
(ctr.loadingState.value as Success).response;
|
||||
list.insert(0, item);
|
||||
ctr.loadingState.value = LoadingState.success(list);
|
||||
ctr.loadingState.refresh();
|
||||
ctr.allSelected.value = false;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/user/fav_folder.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
|
||||
class FavController extends CommonController {
|
||||
class FavController
|
||||
extends CommonListController<FavFolderData, FavFolderItemData> {
|
||||
late final dynamic mid = Accounts.main.mid;
|
||||
|
||||
@override
|
||||
@@ -24,22 +24,20 @@ class FavController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
if (response.response.hasMore == false ||
|
||||
(response.response.list as List?).isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
response.response.list ??= <FavFolderItemData>[];
|
||||
response.response.list!
|
||||
.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(response.response.list);
|
||||
return true;
|
||||
List<FavFolderItemData>? getDataList(FavFolderData response) {
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => UserHttp.userfavFolder(
|
||||
bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) {
|
||||
if (response.response.hasMore == false) {
|
||||
isEnd = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<FavFolderData>> customGetData() => UserHttp.userfavFolder(
|
||||
pn: currentPage,
|
||||
ps: 10,
|
||||
mid: mid,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/user/fav_folder.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -44,7 +45,7 @@ class _FavVideoPageState extends State<FavVideoPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<FavFolderItemData>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
@@ -59,7 +60,7 @@ class _FavVideoPageState extends State<FavVideoPage>
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
@@ -72,33 +73,31 @@ class _FavVideoPageState extends State<FavVideoPage>
|
||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
(BuildContext context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_favController.onLoadMore();
|
||||
}
|
||||
String heroTag =
|
||||
Utils.makeHeroTag(loadingState.response[index].fid);
|
||||
final item = loadingState.response![index];
|
||||
String heroTag = Utils.makeHeroTag(item.fid);
|
||||
return FavItem(
|
||||
heroTag: heroTag,
|
||||
favFolderItem: loadingState.response[index],
|
||||
favFolderItem: item,
|
||||
onTap: () async {
|
||||
dynamic res = await Get.toNamed(
|
||||
'/favDetail',
|
||||
arguments: loadingState.response[index],
|
||||
arguments: item,
|
||||
parameters: {
|
||||
'heroTag': heroTag,
|
||||
'mediaId':
|
||||
loadingState.response[index].id.toString(),
|
||||
'mediaId': item.id.toString(),
|
||||
},
|
||||
);
|
||||
if (res == true) {
|
||||
List list =
|
||||
List<FavFolderItemData> list =
|
||||
(_favController.loadingState.value as Success)
|
||||
.response;
|
||||
list.removeAt(index);
|
||||
_favController.loadingState.value =
|
||||
LoadingState.success(list);
|
||||
_favController.loadingState.refresh();
|
||||
} else {
|
||||
Future.delayed(const Duration(milliseconds: 255), () {
|
||||
_favController.onRefresh();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/user/fav_folder.dart';
|
||||
import 'package:PiliPlus/pages/fav/article/view.dart';
|
||||
import 'package:PiliPlus/pages/fav/note/view.dart';
|
||||
import 'package:PiliPlus/pages/fav/pgc/view.dart';
|
||||
@@ -54,13 +55,13 @@ class _FavPageState extends State<FavPage> with SingleTickerProviderStateMixin {
|
||||
Get.toNamed('/createFav')?.then(
|
||||
(data) {
|
||||
if (data != null) {
|
||||
List list = _favController.loadingState.value is Success
|
||||
List<FavFolderItemData> list =
|
||||
_favController.loadingState.value is Success
|
||||
? (_favController.loadingState.value as Success)
|
||||
.response
|
||||
: [];
|
||||
: <FavFolderItemData>[];
|
||||
list.insert(list.isNotEmpty ? 1 : 0, data);
|
||||
_favController.loadingState.value =
|
||||
LoadingState.success(list);
|
||||
_favController.loadingState.refresh();
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -81,6 +82,7 @@ class _FavPageState extends State<FavPage> with SingleTickerProviderStateMixin {
|
||||
'title': item.title,
|
||||
'count': item.mediaCount,
|
||||
'searchType': SearchType.fav,
|
||||
'isOwner': true,
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/models/user/fav_detail.dart';
|
||||
import 'package:PiliPlus/models/user/fav_folder.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -11,7 +10,8 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
|
||||
class FavDetailController extends MultiSelectController {
|
||||
class FavDetailController
|
||||
extends MultiSelectController<FavDetailData, FavDetailItemData> {
|
||||
Rx<FavFolderItemData> item = FavFolderItemData().obs;
|
||||
int? mediaId;
|
||||
late String heroTag;
|
||||
@@ -35,40 +35,39 @@ class FavDetailController extends MultiSelectController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
List<FavDetailItemData>? getDataList(FavDetailData response) {
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (item.value.mediaCount != null && length >= item.value.mediaCount!) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<FavDetailData> response) {
|
||||
FavDetailData data = response.response;
|
||||
if (currentPage == 1) {
|
||||
if (isRefresh) {
|
||||
item.value = data.info ?? FavFolderItemData();
|
||||
isOwner.value = data.info?.mid == mid;
|
||||
}
|
||||
if (data.list.isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
data.list ??= <FavDetailItemData>[];
|
||||
data.list!.insertAll(
|
||||
0,
|
||||
(loadingState.value as Success).response,
|
||||
);
|
||||
}
|
||||
if (isEnd.not && (data.list?.length ?? 0) >= (data.info?.mediaCount ?? 0)) {
|
||||
isEnd = true;
|
||||
}
|
||||
loadingState.value = LoadingState.success(data.list);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
onCancelFav(int id, int type) async {
|
||||
onCancelFav(int index, int id, int type) async {
|
||||
var result = await VideoHttp.delFav(
|
||||
ids: ['$id:$type'],
|
||||
delIds: mediaId.toString(),
|
||||
);
|
||||
if (result['status']) {
|
||||
List dataList = (loadingState.value as Success).response;
|
||||
dataList.removeWhere((item) => item.id == id);
|
||||
List<FavDetailItemData> dataList =
|
||||
(loadingState.value as Success).response;
|
||||
item.value.mediaCount = item.value.mediaCount! - 1;
|
||||
item.refresh();
|
||||
loadingState.value = LoadingState.success(dataList);
|
||||
dataList.removeAt(index);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('取消收藏');
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
@@ -76,7 +75,8 @@ class FavDetailController extends MultiSelectController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => UserHttp.userFavFolderDetail(
|
||||
Future<LoadingState<FavDetailData>> customGetData() =>
|
||||
UserHttp.userFavFolderDetail(
|
||||
pn: currentPage,
|
||||
ps: 20,
|
||||
mediaId: mediaId!,
|
||||
@@ -110,8 +110,9 @@ class FavDetailController extends MultiSelectController {
|
||||
delIds: mediaId.toString(),
|
||||
);
|
||||
if (result['status']) {
|
||||
List dataList = (loadingState.value as Success).response;
|
||||
List remainList =
|
||||
List<FavDetailItemData> dataList =
|
||||
(loadingState.value as Success).response;
|
||||
List<FavDetailItemData> remainList =
|
||||
dataList.toSet().difference(list.toSet()).toList();
|
||||
item.value.mediaCount = item.value.mediaCount! - list.length;
|
||||
item.refresh();
|
||||
@@ -137,8 +138,7 @@ class FavDetailController extends MultiSelectController {
|
||||
|
||||
void toViewPlayAll() {
|
||||
if (loadingState.value is Success) {
|
||||
List<FavDetailItemData> list = List<FavDetailItemData>.from(
|
||||
(loadingState.value as Success).response);
|
||||
List<FavDetailItemData> list = (loadingState.value as Success).response;
|
||||
for (FavDetailItemData element in list) {
|
||||
if (element.cid == null) {
|
||||
continue;
|
||||
|
||||
@@ -22,7 +22,7 @@ class _FavSortPageState extends State<FavSortPage> {
|
||||
FavDetailController get _favDetailController => widget.favDetailController;
|
||||
|
||||
final GlobalKey _key = GlobalKey();
|
||||
late List<FavDetailItemData> list = List<FavDetailItemData>.from(
|
||||
late List<FavDetailItemData> sortList = List<FavDetailItemData>.from(
|
||||
(_favDetailController.loadingState.value as Success).response);
|
||||
List<String> sort = <String>[];
|
||||
|
||||
@@ -39,7 +39,7 @@ class _FavSortPageState extends State<FavSortPage> {
|
||||
if (_favDetailController.loadingState.value is Success) {
|
||||
List<FavDetailItemData> list =
|
||||
(_favDetailController.loadingState.value as Success).response;
|
||||
this.list.addAll(list.sublist(this.list.length));
|
||||
this.sortList.addAll(list.sublist(this.sortList.length));
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
@@ -83,7 +83,7 @@ class _FavSortPageState extends State<FavSortPage> {
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast('排序完成');
|
||||
_favDetailController.loadingState.value =
|
||||
LoadingState.success(list);
|
||||
LoadingState.success(sortList);
|
||||
Get.back();
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
@@ -103,14 +103,14 @@ class _FavSortPageState extends State<FavSortPage> {
|
||||
newIndex -= 1;
|
||||
}
|
||||
|
||||
final oldItem = list[oldIndex];
|
||||
final oldItem = sortList[oldIndex];
|
||||
final newItem =
|
||||
list.getOrNull(oldIndex > newIndex ? newIndex - 1 : newIndex);
|
||||
sortList.getOrNull(oldIndex > newIndex ? newIndex - 1 : newIndex);
|
||||
sort.add(
|
||||
'${newItem == null ? '0:0' : '${newItem.id}:${newItem.type}'}:${oldItem.id}:${oldItem.type}');
|
||||
|
||||
final tabsItem = list.removeAt(oldIndex);
|
||||
list.insert(newIndex, tabsItem);
|
||||
final tabsItem = sortList.removeAt(oldIndex);
|
||||
sortList.insert(newIndex, tabsItem);
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
@@ -124,7 +124,7 @@ class _FavSortPageState extends State<FavSortPage> {
|
||||
footer: SizedBox(
|
||||
height: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
children: list
|
||||
children: sortList
|
||||
.map(
|
||||
(item) => Stack(
|
||||
key: Key(item.id.toString()),
|
||||
|
||||
@@ -135,7 +135,8 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
visualDensity:
|
||||
VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
onPressed: () =>
|
||||
Utils.onCopyOrMove<FavDetailItemData>(
|
||||
context: context,
|
||||
isCopy: true,
|
||||
ctr: _favDetailController,
|
||||
@@ -155,7 +156,8 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
visualDensity:
|
||||
VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
onPressed: () =>
|
||||
Utils.onCopyOrMove<FavDetailItemData>(
|
||||
context: context,
|
||||
isCopy: false,
|
||||
ctr: _favDetailController,
|
||||
@@ -188,15 +190,18 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
: [
|
||||
IconButton(
|
||||
tooltip: '搜索',
|
||||
onPressed: () =>
|
||||
Get.toNamed('/favSearch', arguments: {
|
||||
onPressed: () => Get.toNamed(
|
||||
'/favSearch',
|
||||
arguments: {
|
||||
'type': 0,
|
||||
'mediaId': int.parse(mediaId),
|
||||
'title': _favDetailController.item.value.title,
|
||||
'count':
|
||||
_favDetailController.item.value.mediaCount,
|
||||
'searchType': SearchType.fav,
|
||||
}),
|
||||
'isOwner': _favDetailController.isOwner.value,
|
||||
},
|
||||
),
|
||||
icon: const Icon(Icons.search_outlined),
|
||||
),
|
||||
// IconButton(
|
||||
@@ -416,7 +421,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<FavDetailItemData>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
@@ -431,7 +436,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 85,
|
||||
@@ -444,7 +449,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length) {
|
||||
if (index == loadingState.response!.length) {
|
||||
_favDetailController.onLoadMore();
|
||||
return Container(
|
||||
height: 60,
|
||||
@@ -458,25 +463,26 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
FavDetailItemData element = loadingState.response[index];
|
||||
FavDetailItemData item = loadingState.response![index];
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: FavVideoCardH(
|
||||
videoItem: element,
|
||||
videoItem: item,
|
||||
callFn: () => _favDetailController.onCancelFav(
|
||||
element.id!,
|
||||
element.type!,
|
||||
index,
|
||||
item.id!,
|
||||
item.type!,
|
||||
),
|
||||
onViewFav: () {
|
||||
Utils.toViewPage(
|
||||
'bvid=${element.bvid}&cid=${element.cid}',
|
||||
'bvid=${item.bvid}&cid=${item.cid}',
|
||||
arguments: {
|
||||
'videoItem': element,
|
||||
'heroTag': Utils.makeHeroTag(element.bvid),
|
||||
'videoItem': item,
|
||||
'heroTag': Utils.makeHeroTag(item.bvid),
|
||||
'sourceType': 'fav',
|
||||
'mediaId': _favDetailController.item.value.id,
|
||||
'oid': element.id,
|
||||
'oid': item.id,
|
||||
'favTitle':
|
||||
_favDetailController.item.value.title,
|
||||
'count': _favDetailController
|
||||
@@ -513,10 +519,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
AnimatedOpacity(
|
||||
opacity:
|
||||
loadingState.response[index].checked == true
|
||||
? 1
|
||||
: 0,
|
||||
opacity: item.checked == true ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
@@ -531,11 +534,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
width: 34,
|
||||
height: 34,
|
||||
child: AnimatedScale(
|
||||
scale: loadingState
|
||||
.response[index].checked ==
|
||||
true
|
||||
? 1
|
||||
: 0,
|
||||
scale: item.checked == true ? 1 : 0,
|
||||
duration:
|
||||
const Duration(milliseconds: 250),
|
||||
curve: Curves.easeInOut,
|
||||
@@ -571,7 +570,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
],
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length + 1,
|
||||
childCount: loadingState.response!.length + 1,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/fav_search/view.dart' show SearchType;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
@@ -9,7 +9,7 @@ import 'package:PiliPlus/http/user.dart';
|
||||
|
||||
import '../../http/video.dart';
|
||||
|
||||
class FavSearchController extends CommonController {
|
||||
class FavSearchController extends CommonListController {
|
||||
final controller = TextEditingController();
|
||||
final searchFocusNode = FocusNode();
|
||||
|
||||
@@ -17,6 +17,7 @@ class FavSearchController extends CommonController {
|
||||
int? mediaId;
|
||||
int? mid;
|
||||
late SearchType searchType;
|
||||
final bool? isOwner = Get.arguments['isOwner'];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -45,23 +46,19 @@ class FavSearchController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
late List currentList = loadingState.value is Success
|
||||
? (loadingState.value as Success).response
|
||||
: [];
|
||||
List? dataList = currentPage == 1
|
||||
? response.response.list
|
||||
: response.response.list != null
|
||||
? currentList + response.response.list
|
||||
: currentList;
|
||||
List? getDataList(response) {
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
isEnd = searchType == SearchType.fav
|
||||
? response.response.hasMore == false
|
||||
: response.response.list == null || response.response.list.isEmpty;
|
||||
loadingState.value = LoadingState.success(dataList);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
onCancelFav(int id, int type) async {
|
||||
onCancelFav(int index, int id, int type) async {
|
||||
var result = await VideoHttp.favVideo(
|
||||
aid: id,
|
||||
addIds: '',
|
||||
@@ -70,8 +67,8 @@ class FavSearchController extends CommonController {
|
||||
);
|
||||
if (result['status']) {
|
||||
List dataList = (loadingState.value as Success).response;
|
||||
dataList.removeWhere((item) => item.id == id);
|
||||
loadingState.value = LoadingState.success(dataList);
|
||||
dataList.removeAt(index);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('取消收藏');
|
||||
}
|
||||
}
|
||||
@@ -104,7 +101,7 @@ class FavSearchController extends CommonController {
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future delHistory(kid, business) async {
|
||||
Future onDelHistory(index, kid, business) async {
|
||||
String resKid = 'archive_$kid';
|
||||
if (business == 'live') {
|
||||
resKid = 'live_$kid';
|
||||
@@ -115,8 +112,8 @@ class FavSearchController extends CommonController {
|
||||
var res = await UserHttp.delHistory([resKid]);
|
||||
if (res['status']) {
|
||||
List historyList = (loadingState.value as Success).response;
|
||||
historyList.removeWhere((e) => e.kid == kid);
|
||||
loadingState.value = LoadingState.success(historyList);
|
||||
historyList.removeAt(index);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,12 +61,12 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<dynamic>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => errorWidget(),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? switch (_favSearchCtr.searchType) {
|
||||
SearchType.fav => CustomScrollView(
|
||||
SearchType.fav || SearchType.history => CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _favSearchCtr.scrollController,
|
||||
slivers: [
|
||||
@@ -82,30 +82,34 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_favSearchCtr.onLoadMore();
|
||||
}
|
||||
final element = loadingState.response[index];
|
||||
return FavVideoCardH(
|
||||
videoItem: element,
|
||||
final item = loadingState.response![index];
|
||||
return _favSearchCtr.searchType == SearchType.fav
|
||||
? FavVideoCardH(
|
||||
videoItem: item,
|
||||
isOwner: _favSearchCtr.isOwner ?? false,
|
||||
searchType: _favSearchCtr.type,
|
||||
callFn: _favSearchCtr.type != 1
|
||||
? () {
|
||||
_favSearchCtr.onCancelFav(
|
||||
element.id!,
|
||||
element.type,
|
||||
index,
|
||||
item.id!,
|
||||
item.type,
|
||||
);
|
||||
}
|
||||
: null,
|
||||
onViewFav: () {
|
||||
Utils.toViewPage(
|
||||
'bvid=${element.bvid}&cid=${element.cid}',
|
||||
'bvid=${item.bvid}&cid=${item.cid}',
|
||||
arguments: {
|
||||
'videoItem': element,
|
||||
'heroTag': Utils.makeHeroTag(element.bvid),
|
||||
'videoItem': item,
|
||||
'heroTag':
|
||||
Utils.makeHeroTag(item.bvid),
|
||||
'sourceType': 'fav',
|
||||
'mediaId': Get.arguments['mediaId'],
|
||||
'oid': element.id,
|
||||
'oid': item.id,
|
||||
'favTitle': Get.arguments['title'],
|
||||
'count': Get.arguments['count'],
|
||||
'desc': true,
|
||||
@@ -113,9 +117,18 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
: HistoryItem(
|
||||
videoItem: item,
|
||||
ctr: _favSearchCtr,
|
||||
onChoose: null,
|
||||
onDelete: (kid, business) {
|
||||
_favSearchCtr.onDelHistory(
|
||||
index, kid, business);
|
||||
},
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -126,47 +139,16 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
controller: _favSearchCtr.scrollController,
|
||||
itemCount: loadingState.response.length,
|
||||
itemCount: loadingState.response!.length,
|
||||
itemBuilder: ((context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_favSearchCtr.onLoadMore();
|
||||
}
|
||||
return FollowItem(
|
||||
item: loadingState.response[index],
|
||||
item: loadingState.response![index],
|
||||
);
|
||||
}),
|
||||
),
|
||||
SearchType.history => CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _favSearchCtr.scrollController,
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: 2,
|
||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
_favSearchCtr.onLoadMore();
|
||||
}
|
||||
return HistoryItem(
|
||||
videoItem: loadingState.response[index],
|
||||
ctr: _favSearchCtr,
|
||||
onChoose: null,
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
}
|
||||
: errorWidget(
|
||||
callback: _favSearchCtr.onReload,
|
||||
|
||||
@@ -37,12 +37,16 @@ class _FollowPageState extends State<FollowPage> {
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => Get.toNamed('/favSearch', arguments: {
|
||||
onPressed: () => Get.toNamed(
|
||||
'/favSearch',
|
||||
arguments: {
|
||||
'mid': int.parse(mid),
|
||||
'searchType': SearchType.follow,
|
||||
}),
|
||||
},
|
||||
),
|
||||
icon: const Icon(Icons.search_outlined),
|
||||
tooltip: '搜索'),
|
||||
tooltip: '搜索',
|
||||
),
|
||||
PopupMenuButton(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/models/user/history.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
|
||||
class HistoryController extends MultiSelectController
|
||||
class HistoryController extends MultiSelectController<HistoryData, HisListItem>
|
||||
with GetTickerProviderStateMixin {
|
||||
HistoryController(this.type);
|
||||
|
||||
@@ -37,25 +37,27 @@ class HistoryController extends MultiSelectController
|
||||
}
|
||||
|
||||
@override
|
||||
onSelect(int index) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
list[index].checked = !(list[index]?.checked ?? false);
|
||||
onSelect(int index, [bool disableSelect = true]) {
|
||||
List<HisListItem> list = (loadingState.value as Success).response;
|
||||
list[index].checked = !(list[index].checked ?? false);
|
||||
baseCtr.checkedCount.value =
|
||||
list.where((item) => item.checked == true).length;
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
if (baseCtr.checkedCount.value == 0) {
|
||||
baseCtr.enableMultiSelect.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleSelect([bool checked = false]) {
|
||||
void handleSelect([bool checked = false, bool disableSelect = true]) {
|
||||
if (loadingState.value is Success) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
if (list.isNotEmpty) {
|
||||
loadingState.value = LoadingState.success(
|
||||
list.map((item) => item..checked = checked).toList());
|
||||
List<HisListItem>? list = (loadingState.value as Success).response;
|
||||
if (list?.isNotEmpty == true) {
|
||||
for (HisListItem item in list!) {
|
||||
item.checked = checked;
|
||||
}
|
||||
baseCtr.checkedCount.value = checked ? list.length : 0;
|
||||
loadingState.refresh();
|
||||
}
|
||||
}
|
||||
if (checked.not) {
|
||||
@@ -64,26 +66,28 @@ class HistoryController extends MultiSelectController
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
List<HisListItem>? getDataList(HistoryData response) {
|
||||
return response.list;
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<HistoryData> response) {
|
||||
HistoryData data = response.response;
|
||||
isEnd = data.list.isNullOrEmpty;
|
||||
max = data.list?.lastOrNull?.history.oid;
|
||||
viewAt = data.list?.lastOrNull?.viewAt;
|
||||
if (currentPage == 1) {
|
||||
if (type == null && tabs.isEmpty && data.tab?.isNotEmpty == true) {
|
||||
|
||||
if (isRefresh && type == null) {
|
||||
if (tabs.isEmpty && data.tab?.isNotEmpty == true) {
|
||||
tabs.value = data.tab!;
|
||||
tabController =
|
||||
TabController(length: data.tab!.length + 1, vsync: this);
|
||||
}
|
||||
} else if (loadingState.value is Success) {
|
||||
data.list ??= <HisListItem>[];
|
||||
data.list!.insertAll(
|
||||
0,
|
||||
List<HisListItem>.from((loadingState.value as Success).response),
|
||||
tabController = TabController(
|
||||
length: data.tab!.length + 1,
|
||||
vsync: this,
|
||||
);
|
||||
}
|
||||
loadingState.value = LoadingState.success(data.list);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 观看历史暂停状态
|
||||
@@ -125,7 +129,8 @@ class HistoryController extends MultiSelectController
|
||||
}).toList();
|
||||
dynamic response = await UserHttp.delHistory(kidList);
|
||||
if (response['status']) {
|
||||
List remainList = ((loadingState.value as Success).response as List)
|
||||
List<HisListItem> remainList =
|
||||
((loadingState.value as Success).response as List<HisListItem>)
|
||||
.toSet()
|
||||
.difference(result.toSet())
|
||||
.toList();
|
||||
@@ -179,7 +184,7 @@ class HistoryController extends MultiSelectController
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<HistoryData>> customGetData() =>
|
||||
UserHttp.historyList(type: type ?? 'all', max: max, viewAt: viewAt);
|
||||
|
||||
@override
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/user/history.dart';
|
||||
import 'package:PiliPlus/pages/fav_search/view.dart' show SearchType;
|
||||
import 'package:PiliPlus/pages/history/base_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
@@ -261,7 +262,7 @@ class _HistoryPageState extends State<HistoryPage>
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<HisListItem>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
@@ -276,7 +277,7 @@ class _HistoryPageState extends State<HistoryPage>
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
@@ -290,18 +291,18 @@ class _HistoryPageState extends State<HistoryPage>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_historyController.onLoadMore();
|
||||
}
|
||||
return HistoryItem(
|
||||
videoItem: loadingState.response[index],
|
||||
videoItem: loadingState.response![index],
|
||||
ctr: _historyController.baseCtr,
|
||||
onChoose: () => _historyController.onSelect(index),
|
||||
onDelete: (kid, business) =>
|
||||
_historyController.delHistory(kid, business),
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -24,14 +24,14 @@ class HistoryItem extends StatelessWidget {
|
||||
final HisListItem videoItem;
|
||||
final dynamic ctr;
|
||||
final Function? onChoose;
|
||||
final Function? onDelete;
|
||||
final Function(dynamic kid, dynamic business) onDelete;
|
||||
|
||||
const HistoryItem({
|
||||
super.key,
|
||||
required this.videoItem,
|
||||
this.ctr,
|
||||
this.onChoose,
|
||||
this.onDelete,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -380,10 +380,8 @@ class HistoryItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
onTap: () => onDelete != null
|
||||
? onDelete!(videoItem.kid, videoItem.history.business)
|
||||
: ctr.delHistory(
|
||||
videoItem.kid, videoItem.history.business),
|
||||
onTap: () =>
|
||||
onDelete(videoItem.kid, videoItem.history.business),
|
||||
height: 35,
|
||||
child: const Row(
|
||||
children: [
|
||||
|
||||
@@ -19,7 +19,7 @@ class HomePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage>
|
||||
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
final HomeController _homeController = Get.put(HomeController());
|
||||
final MainController _mainController = Get.put(MainController());
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/model_hot_video_item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class HotController extends CommonController {
|
||||
class HotController
|
||||
extends CommonListController<List<HotVideoItemModel>, HotVideoItemModel> {
|
||||
// int idx = 0;
|
||||
|
||||
late RxBool showHotRcmd = GStorage.showHotRcmd.obs;
|
||||
@@ -22,7 +24,8 @@ class HotController extends CommonController {
|
||||
// }
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => VideoHttp.hotVideoList(
|
||||
Future<LoadingState<List<HotVideoItemModel>>> customGetData() =>
|
||||
VideoHttp.hotVideoList(
|
||||
pn: currentPage,
|
||||
ps: 20,
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/video_card_h.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/tab_type.dart';
|
||||
import 'package:PiliPlus/models/model_hot_video_item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_page.dart';
|
||||
import 'package:PiliPlus/pages/rank/view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -160,10 +161,10 @@ class _HotPageState extends CommonPageState<HotPage, HotController>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<HotVideoItemModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => _buildSkeleton(),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: 2,
|
||||
@@ -172,15 +173,15 @@ class _HotPageState extends CommonPageState<HotPage, HotController>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
controller.onLoadMore();
|
||||
}
|
||||
return VideoCardH(
|
||||
videoItem: loadingState.response[index],
|
||||
videoItem: loadingState.response![index],
|
||||
showPubdate: true,
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
)
|
||||
: HttpError(
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'package:PiliPlus/http/html.dart';
|
||||
import 'package:PiliPlus/http/reply.dart';
|
||||
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
|
||||
class HtmlRenderController extends ReplyController {
|
||||
class HtmlRenderController extends ReplyController<MainListReply> {
|
||||
late String id;
|
||||
late String dynamicType;
|
||||
late int type;
|
||||
@@ -91,7 +91,12 @@ class HtmlRenderController extends ReplyController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() {
|
||||
List<ReplyInfo>? getDataList(MainListReply response) {
|
||||
return response.replies;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<MainListReply>> customGetData() {
|
||||
return ReplyHttp.replyListGrpc(
|
||||
type: type,
|
||||
oid: oid.value,
|
||||
|
||||
@@ -763,7 +763,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget replyList(LoadingState loadingState) {
|
||||
Widget replyList(LoadingState<List<ReplyInfo>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverList.builder(
|
||||
itemCount: 5,
|
||||
@@ -771,11 +771,11 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
||||
return const VideoReplySkeleton();
|
||||
},
|
||||
),
|
||||
Success() => (loadingState.response.replies as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverList.builder(
|
||||
itemCount: loadingState.response.replies.length + 1,
|
||||
itemCount: loadingState.response!.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == loadingState.response.replies.length) {
|
||||
if (index == loadingState.response!.length) {
|
||||
_htmlRenderCtr.onLoadMore();
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
@@ -785,7 +785,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
||||
child: Text(
|
||||
_htmlRenderCtr.isEnd.not
|
||||
? '加载中...'
|
||||
: loadingState.response.replies.isEmpty
|
||||
: loadingState.response!.isEmpty
|
||||
? '还没有评论'
|
||||
: '没有更多了',
|
||||
style: TextStyle(
|
||||
@@ -796,19 +796,20 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
||||
);
|
||||
} else {
|
||||
return ReplyItemGrpc(
|
||||
replyItem: loadingState.response.replies[index],
|
||||
replyItem: loadingState.response![index],
|
||||
replyLevel: '1',
|
||||
replyReply: (replyItem, id) =>
|
||||
replyReply(context, replyItem, id),
|
||||
onReply: () {
|
||||
_htmlRenderCtr.onReply(
|
||||
context,
|
||||
replyItem: loadingState.response.replies[index],
|
||||
replyItem: loadingState.response![index],
|
||||
index: index,
|
||||
);
|
||||
},
|
||||
onDelete: _htmlRenderCtr.onMDelete,
|
||||
upMid: loadingState.response.subjectControl.upMid,
|
||||
onDelete: (subIndex) =>
|
||||
_htmlRenderCtr.onRemove(index, subIndex),
|
||||
upMid: _htmlRenderCtr.upMid,
|
||||
callback: _getImageCallback,
|
||||
onCheckReply: (item) =>
|
||||
_htmlRenderCtr.onCheckReply(context, item),
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
|
||||
class LaterController extends MultiSelectController {
|
||||
class LaterController extends MultiSelectController<Map, HotVideoItemModel> {
|
||||
RxInt count = (-1).obs;
|
||||
|
||||
dynamic mid;
|
||||
@@ -22,25 +22,24 @@ class LaterController extends MultiSelectController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
count.value = response.response['count'];
|
||||
if (response.response['list'].isEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
response.response['list'].insertAll(
|
||||
0,
|
||||
List<HotVideoItemModel>.from((loadingState.value as Success).response),
|
||||
);
|
||||
}
|
||||
if (response.response['list'].length >= count.value) {
|
||||
isEnd = true;
|
||||
}
|
||||
loadingState.value = LoadingState.success(response.response['list']);
|
||||
return true;
|
||||
List<HotVideoItemModel>? getDataList(response) {
|
||||
return response['list'];
|
||||
}
|
||||
|
||||
Future toViewDel(BuildContext context, {int? aid}) async {
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (length >= count.value) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
count.value = response.response['count'];
|
||||
return false;
|
||||
}
|
||||
|
||||
Future toViewDel(BuildContext context, {index, aid}) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
@@ -50,7 +49,7 @@ class LaterController extends MultiSelectController {
|
||||
aid != null ? '即将移除该视频,确定是否移除' : '即将删除所有已观看视频,此操作不可恢复。确定是否删除?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
@@ -62,10 +61,11 @@ class LaterController extends MultiSelectController {
|
||||
await UserHttp.toViewDel(aids: aid != null ? [aid] : null);
|
||||
if (res['status']) {
|
||||
if (aid != null) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
list.removeWhere((e) => e.aid == aid);
|
||||
List<HotVideoItemModel> list =
|
||||
(loadingState.value as Success).response;
|
||||
list.removeAt(index);
|
||||
count.value -= 1;
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
} else {
|
||||
onReload();
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class LaterController extends MultiSelectController {
|
||||
onConfirm: () async {
|
||||
var res = await UserHttp.toViewClear();
|
||||
if (res['status']) {
|
||||
loadingState.value = LoadingState.success([]);
|
||||
loadingState.value = LoadingState.success(null);
|
||||
}
|
||||
SmartDialog.showToast(res['msg']);
|
||||
},
|
||||
@@ -98,7 +98,7 @@ class LaterController extends MultiSelectController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => UserHttp.seeYouLater();
|
||||
Future<LoadingState<Map>> customGetData() => UserHttp.seeYouLater();
|
||||
|
||||
onDelChecked(BuildContext context) {
|
||||
showDialog(
|
||||
@@ -137,7 +137,8 @@ class LaterController extends MultiSelectController {
|
||||
List aids = result.map((item) => item.aid).toList();
|
||||
dynamic res = await UserHttp.toViewDel(aids: aids);
|
||||
if (res['status']) {
|
||||
Set remainList = ((loadingState.value as Success).response as List)
|
||||
Set<HotVideoItemModel> remainList =
|
||||
((loadingState.value as Success).response as List<HotVideoItemModel>)
|
||||
.toSet()
|
||||
.difference(result.toSet());
|
||||
count.value -= aids.length;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/model_hot_video_item.dart';
|
||||
import 'package:PiliPlus/pages/history/view.dart' show AppBarWidget;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
@@ -93,7 +94,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
onPressed: () => Utils.onCopyOrMove<HotVideoItemModel>(
|
||||
context: context,
|
||||
isCopy: true,
|
||||
ctr: _laterController,
|
||||
@@ -110,7 +111,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
onPressed: () => Utils.onCopyOrMove<HotVideoItemModel>(
|
||||
context: context,
|
||||
isCopy: false,
|
||||
ctr: _laterController,
|
||||
@@ -171,7 +172,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<HotVideoItemModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
@@ -186,7 +187,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
childCount: 10,
|
||||
),
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: 2,
|
||||
@@ -195,7 +196,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
var videoItem = loadingState.response[index];
|
||||
var videoItem = loadingState.response![index];
|
||||
return Stack(
|
||||
children: [
|
||||
VideoCardH(
|
||||
@@ -209,7 +210,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
'oid': videoItem.aid,
|
||||
'heroTag': Utils.makeHeroTag(videoItem.bvid),
|
||||
'sourceType': 'watchLater',
|
||||
'count': loadingState.response.length,
|
||||
'count': loadingState.response!.length,
|
||||
'favTitle': '稍后再看',
|
||||
'mediaId': _laterController.mid,
|
||||
'desc': false,
|
||||
@@ -293,6 +294,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
onPressed: () {
|
||||
_laterController.toViewDel(
|
||||
context,
|
||||
index: index,
|
||||
aid: videoItem.aid,
|
||||
);
|
||||
},
|
||||
@@ -305,7 +307,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
],
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
)
|
||||
: HttpError(
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/live.dart';
|
||||
import 'package:PiliPlus/models/live/follow.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/models/live/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:get/get_rx/src/rx_types/rx_types.dart';
|
||||
|
||||
class LiveController extends CommonController {
|
||||
class LiveController
|
||||
extends CommonListController<List<LiveItemModel>?, LiveItemModel> {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@@ -17,7 +19,8 @@ class LiveController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => LiveHttp.liveList(pn: currentPage);
|
||||
Future<LoadingState<List<LiveItemModel>?>> customGetData() =>
|
||||
LiveHttp.liveList(pn: currentPage);
|
||||
|
||||
@override
|
||||
Future onRefresh() {
|
||||
@@ -41,20 +44,34 @@ class LiveController extends CommonController {
|
||||
}
|
||||
dynamic res = await LiveHttp.liveFollowing(pn: followPage, ps: 20);
|
||||
if (res['status']) {
|
||||
followPage++;
|
||||
liveCount.value = res['data'].liveCount;
|
||||
List list = res['data']
|
||||
.list
|
||||
.where((LiveFollowingItemModel item) =>
|
||||
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 (isRefresh.not && followListState.value is Success) {
|
||||
list.insertAll(0, (followListState.value as Success).response);
|
||||
if (dataList.isNullOrEmpty) {
|
||||
followEnd = true;
|
||||
if (isRefresh) {
|
||||
followListState.value = LoadingState.success(dataList);
|
||||
}
|
||||
followEnd = list.length >= liveCount.value ||
|
||||
list.isEmpty ||
|
||||
(res['data'].list as List?).isNullOrEmpty;
|
||||
followListState.value = LoadingState.success(list);
|
||||
return;
|
||||
}
|
||||
if (isRefresh) {
|
||||
if (dataList!.length >= liveCount.value) {
|
||||
followEnd = true;
|
||||
}
|
||||
followListState.value = LoadingState.success(dataList);
|
||||
} else if (loadingState.value is Success) {
|
||||
List<LiveFollowingItemModel> list =
|
||||
(loadingState.value as Success).response;
|
||||
list.addAll(dataList!);
|
||||
if (list.length >= liveCount.value) {
|
||||
followEnd = true;
|
||||
}
|
||||
loadingState.refresh();
|
||||
}
|
||||
followPage++;
|
||||
} else {
|
||||
followListState.value = LoadingState.error(res['msg']);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/common/widgets/network_img_layer.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/pages/common/common_page.dart';
|
||||
import 'package:PiliPlus/pages/live/controller.dart';
|
||||
import 'package:PiliPlus/pages/live/widgets/live_item.dart';
|
||||
@@ -66,17 +67,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
top: StyleString.cardSpace,
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: Obx(
|
||||
() => controller.loadingState.value is Loading ||
|
||||
controller.loadingState.value is Success
|
||||
? contentGrid(controller.loadingState.value)
|
||||
: HttpError(
|
||||
errMsg: controller.loadingState.value is Error
|
||||
? (controller.loadingState.value as Error).errMsg
|
||||
: '没有相关数据',
|
||||
callback: controller.onReload,
|
||||
),
|
||||
),
|
||||
sliver: Obx(() => _buildBody(controller.loadingState.value)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -84,33 +75,49 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
);
|
||||
}
|
||||
|
||||
Widget contentGrid(LoadingState loadingState) {
|
||||
return SliverGrid(
|
||||
Widget _buildBody(LoadingState<List<LiveItemModel>?> 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(
|
||||
(BuildContext context, int index) {
|
||||
if (loadingState is Success &&
|
||||
index == loadingState.response.length - 1) {
|
||||
(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 loadingState is Success
|
||||
? LiveCardV(
|
||||
liveItem: loadingState.response[index],
|
||||
)
|
||||
: const VideoCardVSkeleton();
|
||||
return LiveCardV(liveItem: loadingState.response![index]);
|
||||
},
|
||||
childCount: loadingState is Success ? loadingState.response.length : 10,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
);
|
||||
)
|
||||
: scrollErrorWidget(callback: controller.onReload),
|
||||
Error() => HttpError(
|
||||
errMsg: loadingState.errMsg,
|
||||
callback: controller.onReload,
|
||||
),
|
||||
_ => throw UnimplementedError(),
|
||||
};
|
||||
}
|
||||
|
||||
Widget _buildFollowList() {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/models/user/fav_folder.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
|
||||
class MediaController extends CommonController {
|
||||
class MediaController
|
||||
extends CommonDataController<FavFolderData, FavFolderData> {
|
||||
List list = [
|
||||
// {
|
||||
// 'icon': Icons.file_download_outlined,
|
||||
@@ -52,14 +54,14 @@ class MediaController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
count.value = response.response.count;
|
||||
bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) {
|
||||
count.value = response.response.count ?? -1;
|
||||
loadingState.value = response;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() {
|
||||
Future<LoadingState<FavFolderData>> customGetData() {
|
||||
mid ??= Accounts.main.mid;
|
||||
return UserHttp.userfavFolder(
|
||||
pn: 1,
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space_article/item.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/member_article_ctr.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/widget/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
@@ -38,10 +39,10 @@ class _MemberArticleState extends State<MemberArticle>
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState loadingState) {
|
||||
_buildBody(LoadingState<List<Item>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
@@ -56,14 +57,14 @@ class _MemberArticleState extends State<MemberArticle>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return MemberArticleItem(
|
||||
item: loadingState.response[index],
|
||||
item: loadingState.response![index],
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -2,10 +2,9 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/space_article/item.dart';
|
||||
import 'package:PiliPlus/models/space_article/data.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
|
||||
class MemberArticleCtr extends CommonController {
|
||||
class MemberArticleCtr extends CommonListController<Data, Item> {
|
||||
MemberArticleCtr({
|
||||
required this.mid,
|
||||
});
|
||||
@@ -21,24 +20,24 @@ class MemberArticleCtr extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
Data data = response.response;
|
||||
if (data.item.isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
count = data.count ?? -1;
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
data.item ??= <Item>[];
|
||||
data.item!.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
if ((data.item?.length ?? -1) >= count) {
|
||||
isEnd = true;
|
||||
}
|
||||
loadingState.value = LoadingState.success(data.item);
|
||||
return true;
|
||||
List<Item>? getDataList(Data response) {
|
||||
return response.item;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
void checkIsEnd(int length) {
|
||||
if (length >= count) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||
count = response.response.count ?? -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<Data>> customGetData() =>
|
||||
MemberHttp.spaceArticle(mid: mid, page: currentPage);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_member_home.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/bangumi/member_bangumi_ctr.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
@@ -41,10 +42,10 @@ class _MemberBangumiState extends State<MemberBangumi>
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState loadingState) {
|
||||
_buildBody(LoadingState<List<Item>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
@@ -70,14 +71,14 @@ class _MemberBangumiState extends State<MemberBangumi>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return BangumiCardVMemberHome(
|
||||
bangumiItem: loadingState.response[index],
|
||||
bangumiItem: loadingState.response![index],
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,15 +2,14 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/space_archive/data.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
||||
show ContributeType;
|
||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/models/space/data.dart' as space;
|
||||
|
||||
class MemberBangumiCtr extends CommonController {
|
||||
class MemberBangumiCtr extends CommonListController<Data, Item> {
|
||||
MemberBangumiCtr({
|
||||
required this.mid,
|
||||
required this.heroTag,
|
||||
@@ -37,24 +36,19 @@ class MemberBangumiCtr extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
Data data = response.response;
|
||||
if (data.item.isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
data.item ??= <Item>[];
|
||||
data.item!.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
if (isEnd.not && count != null && data.item!.length >= count!) {
|
||||
isEnd = true;
|
||||
}
|
||||
loadingState.value = LoadingState.success(data.item);
|
||||
return true;
|
||||
List<Item>? getDataList(Data response) {
|
||||
return response.item;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.spaceArchive(
|
||||
void checkIsEnd(int length) {
|
||||
if (count != null && length >= count!) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<Data>> customGetData() => MemberHttp.spaceArchive(
|
||||
type: ContributeType.bangumi,
|
||||
mid: mid,
|
||||
pn: currentPage,
|
||||
|
||||
@@ -4,12 +4,12 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/space_fav/datum.dart';
|
||||
import 'package:PiliPlus/models/space_fav/list.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberFavoriteCtr extends CommonController {
|
||||
class MemberFavoriteCtr extends CommonDataController {
|
||||
MemberFavoriteCtr({
|
||||
required this.mid,
|
||||
});
|
||||
@@ -39,7 +39,7 @@ class MemberFavoriteCtr extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
try {
|
||||
List<Datum> res = response.response;
|
||||
first.value = res.first;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
|
||||
class SeasonSeriesController extends CommonController {
|
||||
class SeasonSeriesController extends CommonListController {
|
||||
SeasonSeriesController(this.mid);
|
||||
final int mid;
|
||||
int? count;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -13,16 +14,22 @@ class SeasonSeriesController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
Map data = response.response;
|
||||
List list = ((data['seasons_list'] as List?) ?? []) +
|
||||
((data['series_list'] as List?) ?? []);
|
||||
if (currentPage != 0 && loadingState.value is Success) {
|
||||
list.insertAll(0, (loadingState.value as Success).response);
|
||||
List? getDataList(response) {
|
||||
return ((response['seasons_list'] as List?) ?? []) +
|
||||
((response['series_list'] as List?) ?? []);
|
||||
}
|
||||
isEnd = list.length >= ((data['page']['total'] as int?) ?? 0);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
return true;
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (count != null && length >= count!) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
count = response.response['page']?['total'];
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -39,10 +39,10 @@ class _SeasonSeriesPageState extends State<SeasonSeriesPage>
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<dynamic>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
@@ -58,13 +58,13 @@ class _SeasonSeriesPageState extends State<SeasonSeriesPage>
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
dynamic item = loadingState.response![index];
|
||||
return SeasonSeriesCard(
|
||||
item: loadingState.response[index],
|
||||
item: item,
|
||||
onTap: () {
|
||||
dynamic item = loadingState.response[index];
|
||||
bool isSeason = item['meta']['season_id'] != null;
|
||||
dynamic id = isSeason
|
||||
? item['meta']['season_id']
|
||||
@@ -89,7 +89,7 @@ class _SeasonSeriesPageState extends State<SeasonSeriesPage>
|
||||
},
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -60,10 +60,10 @@ class _MemberVideoState extends State<MemberVideo>
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState loadingState) {
|
||||
_buildBody(LoadingState<List<Item>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
@@ -186,17 +186,17 @@ class _MemberVideoState extends State<MemberVideo>
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (widget.type != ContributeType.season &&
|
||||
index == loadingState.response.length - 1) {
|
||||
index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
final Item item = loadingState.response[index];
|
||||
final Item item = loadingState.response![index];
|
||||
return VideoCardHMemberVideo(
|
||||
key: ValueKey('${item.param}'),
|
||||
videoItem: item,
|
||||
fromViewAid: _controller.fromViewAid,
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:PiliPlus/http/search.dart';
|
||||
import 'package:PiliPlus/models/space_archive/data.dart';
|
||||
import 'package:PiliPlus/models/space_archive/episodic_button.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
||||
show ContributeType;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
@@ -13,7 +13,7 @@ import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberVideoCtr extends CommonController {
|
||||
class MemberVideoCtr extends CommonListController<Data, Item> {
|
||||
MemberVideoCtr({
|
||||
required this.type,
|
||||
required this.mid,
|
||||
@@ -70,7 +70,7 @@ class MemberVideoCtr extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||
Data data = response.response;
|
||||
episodicButton.value = data.episodicButton ?? EpisodicButton();
|
||||
episodicButton.refresh();
|
||||
@@ -105,7 +105,7 @@ class MemberVideoCtr extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.spaceArchive(
|
||||
Future<LoadingState<Data>> customGetData() => MemberHttp.spaceArchive(
|
||||
type: type,
|
||||
mid: mid,
|
||||
aid: type == ContributeType.video
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space/tab2.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -10,7 +10,7 @@ import 'package:get/get.dart';
|
||||
|
||||
import '../../../../../models/space/item.dart';
|
||||
|
||||
class MemberContributeCtr extends CommonController
|
||||
class MemberContributeCtr extends CommonDataController
|
||||
with GetTickerProviderStateMixin {
|
||||
MemberContributeCtr({
|
||||
required this.heroTag,
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel_grpc.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_dynamic/member_dynamic_ctr.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@Deprecated('Use MemberDynamicsPage instead')
|
||||
class MemberDynamic extends StatefulWidget {
|
||||
const MemberDynamic({
|
||||
super.key,
|
||||
required this.mid,
|
||||
});
|
||||
|
||||
final int mid;
|
||||
|
||||
@override
|
||||
State<MemberDynamic> createState() => _MemberDynamicState();
|
||||
}
|
||||
|
||||
class _MemberDynamicState extends State<MemberDynamic>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late final _controller = Get.put(MemberDynamicCtr(mid: widget.mid));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
? refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
},
|
||||
child: ListView.separated(
|
||||
itemCount: loadingState.response.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return DynamicPanelGrpc(
|
||||
item: loadingState.response[index],
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(height: 10),
|
||||
),
|
||||
)
|
||||
: scrollErrorWidget(
|
||||
callback: _controller.onReload,
|
||||
),
|
||||
Error() => scrollErrorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
callback: _controller.onReload,
|
||||
),
|
||||
LoadingState() => throw UnimplementedError(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v2/dynamic.pb.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
|
||||
class MemberDynamicCtr extends CommonController {
|
||||
MemberDynamicCtr({
|
||||
required this.mid,
|
||||
});
|
||||
int mid;
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
DynSpaceRsp res = response.response;
|
||||
isEnd = !res.hasMore;
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
res.list.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(res.list);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.spaceDynamic(
|
||||
mid: mid,
|
||||
page: currentPage,
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/space/data.dart';
|
||||
import 'package:PiliPlus/models/space/item.dart';
|
||||
import 'package:PiliPlus/models/space/tab2.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -20,7 +20,7 @@ extension MemberTabTypeExt on MemberTabType {
|
||||
String get title => ['默认', '首页', '动态', '投稿', '收藏', '番剧'][index];
|
||||
}
|
||||
|
||||
class MemberControllerNew extends CommonController
|
||||
class MemberControllerNew extends CommonDataController<Data, dynamic>
|
||||
with GetTickerProviderStateMixin {
|
||||
MemberControllerNew({required this.mid});
|
||||
int mid;
|
||||
@@ -58,7 +58,7 @@ class MemberControllerNew extends CommonController
|
||||
];
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||
Data data = response.response;
|
||||
username = data.card?.name ?? '';
|
||||
isFollow.value = data.card?.relation?.isFollow == 1;
|
||||
@@ -138,7 +138,7 @@ class MemberControllerNew extends CommonController
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.space(
|
||||
Future<LoadingState<Data>> customGetData() => MemberHttp.space(
|
||||
mid: mid,
|
||||
fromViewAid: fromViewAid,
|
||||
);
|
||||
|
||||
@@ -24,8 +24,7 @@ class MemberPageNew extends StatefulWidget {
|
||||
State<MemberPageNew> createState() => _MemberPageNewState();
|
||||
}
|
||||
|
||||
class _MemberPageNewState extends State<MemberPageNew>
|
||||
with TickerProviderStateMixin {
|
||||
class _MemberPageNewState extends State<MemberPageNew> {
|
||||
late final int _mid;
|
||||
late final String _heroTag;
|
||||
late final MemberControllerNew _userController;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/models/member/coin.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
|
||||
class MemberCoinController extends CommonController {
|
||||
class MemberCoinController extends CommonListController<
|
||||
List<MemberCoinsDataModel>?, MemberCoinsDataModel> {
|
||||
final dynamic mid;
|
||||
MemberCoinController({this.mid});
|
||||
|
||||
@@ -13,6 +15,6 @@ class MemberCoinController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<List<MemberCoinsDataModel>?>> customGetData() =>
|
||||
MemberHttp.getRecentCoinVideo(mid: mid);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/member/coin.dart';
|
||||
import 'package:PiliPlus/pages/member_coin/controller.dart';
|
||||
import 'package:PiliPlus/pages/member_coin/widgets/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
@@ -41,10 +42,10 @@ class _MemberCoinPageState extends State<MemberCoinPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<MemberCoinsDataModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? GridView.builder(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
@@ -59,9 +60,9 @@ class _MemberCoinPageState extends State<MemberCoinPage> {
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: MediaQuery.textScalerOf(context).scale(75),
|
||||
),
|
||||
itemCount: loadingState.response.length,
|
||||
itemCount: loadingState.response!.length,
|
||||
itemBuilder: (context, index) {
|
||||
return MemberCoinsItem(coinItem: loadingState.response[index]);
|
||||
return MemberCoinsItem(coinItem: loadingState.response![index]);
|
||||
},
|
||||
)
|
||||
: scrollErrorWidget(callback: _ctr.onReload),
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class MemberDynamicsController extends CommonController {
|
||||
class MemberDynamicsController
|
||||
extends CommonListController<DynamicsDataModel, DynamicItemModel> {
|
||||
MemberDynamicsController(this.mid);
|
||||
int mid;
|
||||
String offset = '';
|
||||
@@ -32,22 +33,24 @@ class MemberDynamicsController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
DynamicsDataModel data = response.response;
|
||||
offset = data.offset?.isNotEmpty == true ? data.offset! : '-1';
|
||||
if (data.hasMore == false || data.items.isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
data.items ??= <DynamicItemModel>[];
|
||||
data.items?.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(data.items);
|
||||
return true;
|
||||
List<DynamicItemModel>? getDataList(DynamicsDataModel response) {
|
||||
return response.items;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.memberDynamic(
|
||||
bool customHandleResponse(
|
||||
bool isRefresh, Success<DynamicsDataModel> response) {
|
||||
DynamicsDataModel data = response.response;
|
||||
offset = data.offset?.isNotEmpty == true ? data.offset! : '-1';
|
||||
if (data.hasMore == false) {
|
||||
isEnd = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<DynamicsDataModel>> customGetData() =>
|
||||
MemberHttp.memberDynamic(
|
||||
offset: offset,
|
||||
mid: mid,
|
||||
);
|
||||
@@ -55,9 +58,9 @@ class MemberDynamicsController extends CommonController {
|
||||
Future onRemove(dynamicId) async {
|
||||
var res = await MsgHttp.removeDynamic(dynamicId);
|
||||
if (res['status']) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
List<DynamicItemModel> list = (loadingState.value as Success).response;
|
||||
list.removeWhere((item) => item.idStr == dynamicId);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('删除成功');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:PiliPlus/common/skeleton/dynamic_card.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/pages/member_dynamics/index.dart';
|
||||
@@ -56,26 +58,57 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
|
||||
onRefresh: () async {
|
||||
await _memberDynamicController.onRefresh();
|
||||
},
|
||||
child: Obx(
|
||||
() => _memberDynamicController.loadingState.value is Loading
|
||||
? Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: CustomScrollView(
|
||||
child: CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
_buildContent(_memberDynamicController.loadingState.value),
|
||||
Obx(
|
||||
() => _buildContent(_memberDynamicController.loadingState.value),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
_buildContent(LoadingState loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => HttpError(
|
||||
callback: _memberDynamicController.onReload,
|
||||
Widget skeleton() {
|
||||
if (!dynamicsWaterfallFlow) {
|
||||
return SliverCrossAxisGroup(
|
||||
slivers: [
|
||||
const SliverFillRemaining(),
|
||||
SliverConstrainedCrossAxis(
|
||||
maxExtent: Grid.smallCardWidth * 2,
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return const DynamicCardSkeleton();
|
||||
},
|
||||
childCount: 10,
|
||||
),
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
),
|
||||
),
|
||||
const SliverFillRemaining()
|
||||
],
|
||||
);
|
||||
}
|
||||
return SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
crossAxisSpacing: StyleString.cardSpace / 2,
|
||||
mainAxisSpacing: StyleString.cardSpace / 2,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: 50,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return const DynamicCardSkeleton();
|
||||
},
|
||||
childCount: 10,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(LoadingState<List<DynamicItemModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => skeleton(),
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
@@ -83,24 +116,12 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
|
||||
sliver: dynamicsWaterfallFlow
|
||||
? SliverWaterfallFlow.extent(
|
||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||
//cacheExtent: 0.0,
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
// mainAxisSpacing: StyleString.safeSpace,
|
||||
|
||||
/// follow max child trailing layout offset and layout with full cross axis extend
|
||||
/// last child as loadmore item/no more item in [GridView] and [WaterfallFlow]
|
||||
/// with full cross axis extend
|
||||
// LastChildLayoutType.fullCrossAxisExtend,
|
||||
|
||||
/// as foot at trailing and layout with full cross axis extend
|
||||
/// show no more item at trailing when children are not full of viewport
|
||||
/// if children is full of viewport, it's the same as fullCrossAxisExtend
|
||||
// LastChildLayoutType.foot,
|
||||
lastChildLayoutTypeBuilder: (index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_memberDynamicController.onLoadMore();
|
||||
}
|
||||
return index == loadingState.response.length
|
||||
return index == loadingState.response!.length
|
||||
? LastChildLayoutType.foot
|
||||
: LastChildLayoutType.none;
|
||||
},
|
||||
@@ -121,15 +142,16 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index ==
|
||||
loadingState.response!.length - 1) {
|
||||
_memberDynamicController.onLoadMore();
|
||||
}
|
||||
return DynamicPanel(
|
||||
item: loadingState.response[index],
|
||||
item: loadingState.response![index],
|
||||
onRemove: _memberDynamicController.onRemove,
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/models/member/coin.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
|
||||
class MemberLikeController extends CommonController {
|
||||
class MemberLikeController extends CommonListController<
|
||||
List<MemberCoinsDataModel>?, MemberCoinsDataModel> {
|
||||
final dynamic mid;
|
||||
MemberLikeController({this.mid});
|
||||
|
||||
@@ -13,6 +15,6 @@ class MemberLikeController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<List<MemberCoinsDataModel>?>> customGetData() =>
|
||||
MemberHttp.getRecentLikeVideo(mid: mid);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/member/coin.dart';
|
||||
import 'package:PiliPlus/pages/member_coin/widgets/item.dart';
|
||||
import 'package:PiliPlus/pages/member_like/controller.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
@@ -41,10 +42,10 @@ class _MemberLikePageState extends State<MemberLikePage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<MemberCoinsDataModel>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? GridView.builder(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
@@ -59,9 +60,9 @@ class _MemberLikePageState extends State<MemberLikePage> {
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: MediaQuery.textScalerOf(context).scale(75),
|
||||
),
|
||||
itemCount: loadingState.response.length,
|
||||
itemCount: loadingState.response!.length,
|
||||
itemBuilder: (context, index) {
|
||||
return MemberCoinsItem(coinItem: loadingState.response[index]);
|
||||
return MemberCoinsItem(coinItem: loadingState.response![index]);
|
||||
},
|
||||
)
|
||||
: scrollErrorWidget(callback: _ctr.onReload),
|
||||
|
||||
@@ -91,22 +91,31 @@ class MemberSearchController extends GetxController
|
||||
);
|
||||
if (res['status']) {
|
||||
DynamicsDataModel data = res['data'];
|
||||
if (data.hasMore == false || data.items.isNullOrEmpty) {
|
||||
isEndDynamic = true;
|
||||
}
|
||||
if (isRefresh) {
|
||||
List<DynamicItemModel>? items = data.items;
|
||||
dynamicCount.value = data.total ?? 0;
|
||||
}
|
||||
offset = data.offset ?? '';
|
||||
if (isRefresh.not && dynamicState.value is Success) {
|
||||
data.items ??= <DynamicItemModel>[];
|
||||
data.items!.insertAll(0, (dynamicState.value as Success).response);
|
||||
}
|
||||
if (!isEndDynamic && (data.items?.length ?? 0) >= dynamicCount.value) {
|
||||
|
||||
if (data.hasMore == false || items.isNullOrEmpty) {
|
||||
isEndDynamic = true;
|
||||
dynamicState.value = LoadingState.success(items);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRefresh) {
|
||||
if (items!.length >= dynamicCount.value) {
|
||||
isEndDynamic = true;
|
||||
}
|
||||
dynamicState.value = LoadingState.success(items);
|
||||
} else if (dynamicState.value is Success) {
|
||||
List<DynamicItemModel> currentList =
|
||||
(dynamicState.value as Success).response;
|
||||
currentList.addAll(items!);
|
||||
if (currentList.length >= dynamicCount.value) {
|
||||
isEndDynamic = true;
|
||||
}
|
||||
dynamicState.refresh();
|
||||
}
|
||||
dynamicPn++;
|
||||
dynamicState.value = LoadingState.success(data.items);
|
||||
} else if (isRefresh) {
|
||||
dynamicState.value = LoadingState.error(res['msg']);
|
||||
}
|
||||
@@ -124,24 +133,30 @@ class MemberSearchController extends GetxController
|
||||
);
|
||||
if (res['status']) {
|
||||
MemberArchiveDataModel data = res['data'];
|
||||
if (isRefresh) {
|
||||
List<VListItemModel>? vlist = data.list?.vlist;
|
||||
archiveCount.value = data.page?['count'] ?? 0;
|
||||
|
||||
if (vlist.isNullOrEmpty) {
|
||||
isEndArchive = true;
|
||||
archiveState.value = LoadingState.success(vlist);
|
||||
return;
|
||||
}
|
||||
if (data.list == null || data.list!.vlist.isNullOrEmpty) {
|
||||
|
||||
if (isRefresh) {
|
||||
if (vlist!.length >= archiveCount.value) {
|
||||
isEndArchive = true;
|
||||
}
|
||||
if (isRefresh.not && archiveState.value is Success) {
|
||||
data.list ??= ArchiveListModel();
|
||||
data.list!.vlist ??= <VListItemModel>[];
|
||||
data.list!.vlist!
|
||||
.insertAll(0, (archiveState.value as Success).response);
|
||||
archiveState.value = LoadingState.success(vlist);
|
||||
} else if (dynamicState.value is Success) {
|
||||
List<VListItemModel> currentList =
|
||||
(dynamicState.value as Success).response;
|
||||
currentList.addAll(vlist!);
|
||||
if (currentList.length >= archiveCount.value) {
|
||||
isEndDynamic = true;
|
||||
}
|
||||
if (!isEndArchive &&
|
||||
(data.list?.vlist?.length ?? 0) >= archiveCount.value) {
|
||||
isEndArchive = true;
|
||||
archiveState.refresh();
|
||||
}
|
||||
archivePn++;
|
||||
archiveState.value = LoadingState.success(data.list?.vlist);
|
||||
} else if (isRefresh) {
|
||||
archiveState.value = LoadingState.error(res['msg']);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/models/msg/msgfeed_at_me.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class AtMeController extends CommonController {
|
||||
class AtMeController extends CommonListController<MsgFeedAtMe, AtMeItems> {
|
||||
int cursor = -1;
|
||||
int cursorTime = -1;
|
||||
|
||||
@@ -16,19 +15,19 @@ class AtMeController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
List<AtMeItems>? getDataList(MsgFeedAtMe response) {
|
||||
return response.items;
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<MsgFeedAtMe> response) {
|
||||
MsgFeedAtMe data = response.response;
|
||||
if (data.cursor?.isEnd == true || data.items.isNullOrEmpty) {
|
||||
if (data.cursor?.isEnd == true) {
|
||||
isEnd = true;
|
||||
}
|
||||
cursor = data.cursor?.id ?? -1;
|
||||
cursorTime = data.cursor?.time ?? -1;
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
data.items ??= <AtMeItems>[];
|
||||
data.items!.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(data.items);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -39,16 +38,16 @@ class AtMeController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<MsgFeedAtMe>> customGetData() =>
|
||||
MsgHttp.msgFeedAtMe(cursor: cursor, cursorTime: cursorTime);
|
||||
|
||||
Future onRemove(dynamic id, int index) async {
|
||||
try {
|
||||
var res = await MsgHttp.delMsgfeed(2, id);
|
||||
if (res['status']) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
List<AtMeItems> list = (loadingState.value as Success).response;
|
||||
list.removeAt(index);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('删除成功');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/msg/msgfeed_at_me.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -35,20 +36,20 @@ class _AtMePageState extends State<AtMePage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<AtMeItems>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? ListView.separated(
|
||||
itemCount: loadingState.response.length,
|
||||
itemCount: loadingState.response!.length,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||
itemBuilder: (context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_atMeController.onLoadMore();
|
||||
}
|
||||
final item = loadingState.response[index];
|
||||
final item = loadingState.response![index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
String? nativeUri = item.item?.nativeUri;
|
||||
@@ -103,10 +104,9 @@ class _AtMePageState extends State<AtMePage> {
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if ((item.item?.sourceContent as String?)?.isNotEmpty ==
|
||||
true) ...[
|
||||
if (item.item?.sourceContent?.isNotEmpty == true) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(item.item?.sourceContent,
|
||||
Text(item.item!.sourceContent!,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:PiliPlus/common/widgets/pair.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/models/msg/msgfeed_like_me.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class LikeMeController extends CommonController {
|
||||
class LikeMeController extends CommonDataController<MsgFeedLikeMe, dynamic> {
|
||||
int cursor = -1;
|
||||
int cursorTime = -1;
|
||||
|
||||
@@ -17,7 +17,7 @@ class LikeMeController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
bool customHandleResponse(bool isRefresh, Success<MsgFeedLikeMe> response) {
|
||||
MsgFeedLikeMe data = response.response;
|
||||
if (data.total?.cursor?.isEnd == true ||
|
||||
data.total?.items.isNullOrEmpty == true) {
|
||||
@@ -46,7 +46,7 @@ class LikeMeController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<MsgFeedLikeMe>> customGetData() =>
|
||||
MsgHttp.msgFeedLikeMe(cursor: cursor, cursorTime: cursorTime);
|
||||
|
||||
Future onRemove(dynamic id, int index, bool isLatest) async {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/models/msg/msgfeed_reply_me.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class ReplyMeController extends CommonController {
|
||||
class ReplyMeController
|
||||
extends CommonListController<MsgFeedReplyMe, ReplyMeItems> {
|
||||
int cursor = -1;
|
||||
int cursorTime = -1;
|
||||
|
||||
@@ -16,19 +16,19 @@ class ReplyMeController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
MsgFeedReplyMe data = response.response;
|
||||
if (data.cursor?.isEnd == true || data.items.isNullOrEmpty) {
|
||||
List<ReplyMeItems>? getDataList(MsgFeedReplyMe response) {
|
||||
return response.items;
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<MsgFeedReplyMe> response) {
|
||||
final data = response.response;
|
||||
if (data.cursor?.isEnd == true) {
|
||||
isEnd = true;
|
||||
}
|
||||
cursor = data.cursor?.id ?? -1;
|
||||
cursorTime = data.cursor?.time ?? -1;
|
||||
if (currentPage != 1 && loadingState.value is Success) {
|
||||
data.items ??= <ReplyMeItems>[];
|
||||
data.items!.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
loadingState.value = LoadingState.success(data.items);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -39,16 +39,16 @@ class ReplyMeController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<MsgFeedReplyMe>> customGetData() =>
|
||||
MsgHttp.msgFeedReplyMe(cursor: cursor, cursorTime: cursorTime);
|
||||
|
||||
Future onRemove(dynamic id, int index) async {
|
||||
try {
|
||||
var res = await MsgHttp.delMsgfeed(1, id);
|
||||
if (res['status']) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
List<ReplyMeItems> list = (loadingState.value as Success).response;
|
||||
list.removeAt(index);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('删除成功');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
|
||||
@@ -34,21 +34,21 @@ class _ReplyMePageState extends State<ReplyMePage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<ReplyMeItems>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? ListView.separated(
|
||||
itemCount: loadingState.response.length,
|
||||
itemCount: loadingState.response!.length,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||
itemBuilder: (context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_replyMeController.onLoadMore();
|
||||
}
|
||||
|
||||
ReplyMeItems item = loadingState.response[index];
|
||||
ReplyMeItems item = loadingState.response![index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
String? nativeUri = item.item?.nativeUri;
|
||||
@@ -121,12 +121,8 @@ class _ReplyMePageState extends State<ReplyMePage> {
|
||||
Text(item.item?.sourceContent ?? "",
|
||||
style: Theme.of(context).textTheme.bodyMedium),
|
||||
const SizedBox(height: 4),
|
||||
if (loadingState
|
||||
.response[index].item?.targetReplyContent !=
|
||||
null &&
|
||||
loadingState
|
||||
.response[index].item?.targetReplyContent !=
|
||||
"")
|
||||
if (item.item?.targetReplyContent != null &&
|
||||
item.item?.targetReplyContent != "")
|
||||
Text("| ${item.item?.targetReplyContent}",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/models/msg/msgfeed_sys_msg.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
|
||||
class SysMsgController extends CommonController {
|
||||
class SysMsgController
|
||||
extends CommonListController<List<SystemNotifyList>?, SystemNotifyList> {
|
||||
final pageSize = 20;
|
||||
int cursor = -1;
|
||||
|
||||
@@ -41,9 +43,9 @@ class SysMsgController extends CommonController {
|
||||
try {
|
||||
var res = await MsgHttp.delSysMsg(id);
|
||||
if (res['status']) {
|
||||
List list = (loadingState.value as Success).response;
|
||||
List<SystemNotifyList> list = (loadingState.value as Success).response;
|
||||
list.removeAt(index);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
loadingState.refresh();
|
||||
SmartDialog.showToast('删除成功');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
@@ -52,6 +54,6 @@ class SysMsgController extends CommonController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() =>
|
||||
Future<LoadingState<List<SystemNotifyList>?>> customGetData() =>
|
||||
MsgHttp.msgFeedNotify(cursor: cursor, pageSize: pageSize);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/widgets/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/msg/msgfeed_sys_msg.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
@@ -39,21 +40,21 @@ class _SysMsgPageState extends State<SysMsgPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
Widget _buildBody(LoadingState<List<SystemNotifyList>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? ListView.separated(
|
||||
itemCount: loadingState.response.length,
|
||||
itemCount: loadingState.response!.length,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||
itemBuilder: (context, int index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_sysMsgController.onLoadMore();
|
||||
}
|
||||
|
||||
final item = loadingState.response[index];
|
||||
final item = loadingState.response![index];
|
||||
String? content = item.content;
|
||||
if (content != null) {
|
||||
try {
|
||||
|
||||
@@ -10,7 +10,7 @@ class RankPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RankPageState extends State<RankPage>
|
||||
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
|
||||
with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin {
|
||||
final RankController _rankController = Get.put(RankController());
|
||||
|
||||
@override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user