mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
Compare commits
38 Commits
dev
...
edb5ea7a7a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edb5ea7a7a | ||
|
|
b4c1568869 | ||
|
|
83e25ec0bf | ||
|
|
6d55321699 | ||
|
|
26a5b7b7a7 | ||
|
|
f663301eae | ||
|
|
eb9f3cd21c | ||
|
|
05119edacb | ||
|
|
554e96c820 | ||
|
|
40a19f2766 | ||
|
|
b723529d7f | ||
|
|
9f33488248 | ||
|
|
80a4c8c24d | ||
|
|
170b2aa6d9 | ||
|
|
e2639b6951 | ||
|
|
b954c6f893 | ||
|
|
104d295389 | ||
|
|
3caa684b2e | ||
|
|
af7a1a6ee9 | ||
|
|
add519120c | ||
|
|
01552801f2 | ||
|
|
afb09e8a0a | ||
|
|
deb48d1ada | ||
|
|
cf84a92808 | ||
|
|
26ccb92b44 | ||
|
|
3fa697a037 | ||
|
|
f72c13df62 | ||
|
|
7b51f15753 | ||
|
|
d246462535 | ||
|
|
3208661a52 | ||
|
|
2e614fa03c | ||
|
|
b7f70ee0b3 | ||
|
|
cb52840bad | ||
|
|
bd3d6cf34c | ||
|
|
cf835e330b | ||
|
|
14fd660ce2 | ||
|
|
0a8282d3e3 | ||
|
|
574e432e09 |
@@ -47,6 +47,7 @@
|
||||
|
||||
## feat
|
||||
|
||||
- [x] 播放课堂视频
|
||||
- [x] 发起投票
|
||||
- [x] 发布动态/评论支持`富文本编辑`/`表情显示`/`@用户`
|
||||
- [x] 修改消息设置
|
||||
|
||||
@@ -111,9 +111,10 @@
|
||||
android:pathPattern="/readlist" />
|
||||
<data android:host="advertise" android:path="/home" />
|
||||
<data android:host="clip" />
|
||||
<data android:host="search" />
|
||||
<data android:host="search" android:pathPattern=".*" />
|
||||
<data android:host="stardust-search" />
|
||||
<data android:host="music" />
|
||||
<data android:host="cheese" />
|
||||
<data android:host="bangumi"
|
||||
android:pathPattern="/season.*" />
|
||||
<data android:host="bangumi" android:pathPattern="/.*" />
|
||||
@@ -145,7 +146,6 @@
|
||||
<data android:host="video" />
|
||||
<data android:host="story" />
|
||||
<data android:host="podcast" />
|
||||
<data android:host="search" />
|
||||
<data android:host="main" android:path="/favorite" />
|
||||
<data android:host="pgc" android:path="/theater/match" />
|
||||
<data android:host="pgc" android:path="/theater/square" />
|
||||
@@ -160,7 +160,6 @@
|
||||
<data android:host="history" />
|
||||
<data android:host="charge" android:path="/rank" />
|
||||
<data android:host="assistant" />
|
||||
<data android:host="assistant" />
|
||||
<data android:host="feedback" />
|
||||
<data android:host="auth" android:path="/launch" />
|
||||
</intent-filter>
|
||||
|
||||
59
lib/common/widgets/appbar/appbar.dart
Normal file
59
lib/common/widgets/appbar/appbar.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MultiSelectAppBarWidget extends StatelessWidget
|
||||
implements PreferredSizeWidget {
|
||||
final MultiSelectBase ctr;
|
||||
final bool? visible;
|
||||
final AppBar child;
|
||||
final List<Widget>? children;
|
||||
|
||||
const MultiSelectAppBarWidget({
|
||||
super.key,
|
||||
required this.ctr,
|
||||
this.visible,
|
||||
this.children,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (visible ?? ctr.enableMultiSelect.value) {
|
||||
return AppBar(
|
||||
bottom: child.bottom,
|
||||
leading: IconButton(
|
||||
tooltip: '取消',
|
||||
onPressed: ctr.handleSelect,
|
||||
icon: const Icon(Icons.close_outlined),
|
||||
),
|
||||
title: Obx(() => Text('已选: ${ctr.checkedCount}')),
|
||||
actions: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: () => ctr.handleSelect(true),
|
||||
child: const Text('全选'),
|
||||
),
|
||||
...?children,
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: ctr.onRemove,
|
||||
child: Text(
|
||||
'移除',
|
||||
style: TextStyle(color: Get.theme.colorScheme.error),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
],
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => child.preferredSize;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import 'package:PiliPlus/common/widgets/image/nine_grid_view.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/image_preview_type.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -98,12 +99,12 @@ Widget imageView(
|
||||
};
|
||||
}
|
||||
|
||||
void onTap(BuildContext context, int index) {
|
||||
void onTap(int index) {
|
||||
if (callback != null) {
|
||||
callback(picArr.map((item) => item.url).toList(), index);
|
||||
} else {
|
||||
onViewImage?.call();
|
||||
context.imageView(
|
||||
PageUtils.imageView(
|
||||
initialPage: index,
|
||||
imgList: picArr.map(
|
||||
(item) {
|
||||
@@ -138,7 +139,7 @@ Widget imageView(
|
||||
return Hero(
|
||||
tag: item.url,
|
||||
child: GestureDetector(
|
||||
onTap: () => onTap(context, index),
|
||||
onTap: () => onTap(index),
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.center,
|
||||
|
||||
@@ -48,7 +48,7 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
|
||||
this.minScale = 1.0,
|
||||
this.onPageChanged,
|
||||
this.onDismissed,
|
||||
this.setStatusBar,
|
||||
this.setStatusBar = true,
|
||||
this.onClose,
|
||||
required this.quality,
|
||||
});
|
||||
@@ -57,7 +57,7 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
|
||||
|
||||
final ValueChanged<bool>? onClose;
|
||||
|
||||
final bool? setStatusBar;
|
||||
final bool setStatusBar;
|
||||
|
||||
/// The sources to show.
|
||||
final List<SourceModel> sources;
|
||||
@@ -114,7 +114,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
duration: const Duration(milliseconds: 300),
|
||||
)..addListener(listener);
|
||||
|
||||
if (widget.setStatusBar != false) {
|
||||
if (widget.setStatusBar) {
|
||||
setStatusBar();
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
_animationController
|
||||
..removeListener(listener)
|
||||
..dispose();
|
||||
if (widget.setStatusBar != false) {
|
||||
if (widget.setStatusBar) {
|
||||
if (Platform.isIOS || Platform.isAndroid) {
|
||||
SystemChrome.setEnabledSystemUIMode(
|
||||
mode ?? SystemUiMode.edgeToEdge,
|
||||
|
||||
@@ -3,10 +3,10 @@ import 'package:PiliPlus/models/common/avatar_badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/image_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class PendantAvatar extends StatelessWidget {
|
||||
final BadgeType _badgeType;
|
||||
@@ -14,7 +14,7 @@ class PendantAvatar extends StatelessWidget {
|
||||
final double size;
|
||||
final double badgeSize;
|
||||
final String? garbPendantImage;
|
||||
final dynamic roomId;
|
||||
final int? roomId;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const PendantAvatar({
|
||||
@@ -71,7 +71,7 @@ class PendantAvatar extends StatelessWidget {
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
child: InkWell(
|
||||
onTap: () => Get.toNamed('/liveRoom?roomid=$roomId'),
|
||||
onTap: () => PageUtils.toLiveRoom(roomId),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
34
lib/common/widgets/select_mask.dart
Normal file
34
lib/common/widgets/select_mask.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget selectMask(ThemeData theme, bool checked) {
|
||||
return AnimatedOpacity(
|
||||
opacity: checked ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: StyleString.mdRadius,
|
||||
color: Colors.black.withValues(alpha: 0.6),
|
||||
),
|
||||
child: AnimatedScale(
|
||||
scale: checked ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeInOut,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface.withValues(alpha: 0.8),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.done_all_outlined,
|
||||
color: theme.colorScheme.primary,
|
||||
semanticLabel: '取消选择',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class _SelfSizedHorizontalListState extends State<SelfSizedHorizontalList> {
|
||||
padding: widget.padding,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: widget.itemCount,
|
||||
itemBuilder: (c, i) => widget.childBuilder.call(i),
|
||||
itemBuilder: (c, i) => widget.childBuilder(i),
|
||||
separatorBuilder: (c, i) => SizedBox(width: widget.gapSize),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ class StatWidget extends StatelessWidget {
|
||||
Theme.of(context).colorScheme.outline.withValues(alpha: 0.8);
|
||||
return Row(
|
||||
spacing: 2,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
type.iconData,
|
||||
|
||||
@@ -14,10 +14,8 @@ import 'package:PiliPlus/models/search/result.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/duration_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
// 视频卡片 - 水平布局
|
||||
class VideoCardH extends StatelessWidget {
|
||||
@@ -43,6 +41,11 @@ class VideoCardH extends StatelessWidget {
|
||||
var typeOrNull = item.type;
|
||||
if (typeOrNull?.isNotEmpty == true) {
|
||||
type = typeOrNull!;
|
||||
if (type == 'ketang') {
|
||||
badge = '课堂';
|
||||
} else if (type == 'live_room') {
|
||||
badge = '直播';
|
||||
}
|
||||
}
|
||||
if (item.isUnionVideo == 1) {
|
||||
badge = '合作';
|
||||
@@ -71,13 +74,13 @@ class VideoCardH extends StatelessWidget {
|
||||
onTap ??
|
||||
() async {
|
||||
if (type == 'ketang') {
|
||||
SmartDialog.showToast('课堂视频暂不支持播放');
|
||||
PageUtils.viewPugv(seasonId: videoItem.aid);
|
||||
return;
|
||||
} else if (type == 'live_room') {
|
||||
if (videoItem case SearchVideoItemModel item) {
|
||||
int? roomId = item.id;
|
||||
if (roomId != null) {
|
||||
Get.toNamed('/liveRoom?roomid=$roomId');
|
||||
PageUtils.toLiveRoom(roomId);
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(
|
||||
@@ -92,6 +95,7 @@ class VideoCardH extends StatelessWidget {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final int? cid =
|
||||
videoItem.cid ??
|
||||
@@ -101,11 +105,10 @@ class VideoCardH extends StatelessWidget {
|
||||
);
|
||||
if (cid != null) {
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=${videoItem.bvid}&cid=$cid',
|
||||
arguments: {
|
||||
'videoItem': videoItem,
|
||||
'heroTag': Utils.makeHeroTag(videoItem.aid),
|
||||
},
|
||||
bvid: videoItem.bvid,
|
||||
cid: cid,
|
||||
cover: videoItem.cover,
|
||||
title: videoItem.title,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -173,13 +176,6 @@ class VideoCardH extends StatelessWidget {
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
),
|
||||
if (type != 'video')
|
||||
PBadge(
|
||||
text: type,
|
||||
left: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.primary,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -42,11 +42,11 @@ class VideoCardV extends StatelessWidget {
|
||||
await SearchHttp.ab2c(aid: videoItem.aid, bvid: bvid);
|
||||
if (cid != null) {
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=$bvid&cid=$cid',
|
||||
arguments: {
|
||||
'heroTag': heroTag,
|
||||
'videoItem': videoItem,
|
||||
},
|
||||
aid: videoItem.aid,
|
||||
bvid: bvid,
|
||||
cid: cid,
|
||||
cover: videoItem.cover,
|
||||
title: videoItem.title,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -22,6 +22,8 @@ class Api {
|
||||
// https://api.bilibili.com/pgc/player/web/v2/playurl?cid=104236640&bvid=BV13t411n7ex
|
||||
static const String pgcUrl = '/pgc/player/web/v2/playurl';
|
||||
|
||||
static const String pugvUrl = '/pugv/player/web/playurl';
|
||||
|
||||
// 字幕
|
||||
// aid, cid
|
||||
static const String playInfo = '/x/player/wbi/v2';
|
||||
@@ -279,6 +281,8 @@ class Api {
|
||||
// 番剧/剧集明细
|
||||
static const String pgcInfo = '/pgc/view/web/season';
|
||||
|
||||
static const String pugvInfo = '/pugv/view/web/season';
|
||||
|
||||
// https://api.bilibili.com/pgc/season/episode/web/info?ep_id=12345678
|
||||
static const String episodeInfo = '/pgc/season/episode/web/info';
|
||||
|
||||
@@ -846,6 +850,12 @@ class Api {
|
||||
|
||||
static const String dynReserve = '/x/dynamic/feed/reserve/click';
|
||||
|
||||
static const String favPugv = '/pugv/app/web/favorite/page';
|
||||
|
||||
static const String addFavPugv = '/pugv/app/web/favorite/add';
|
||||
|
||||
static const String delFavPugv = '/pugv/app/web/favorite/del';
|
||||
|
||||
static const String favTopicList = '/x/topic/web/fav/list';
|
||||
|
||||
static const String addFavTopic = '/x/topic/fav/sub/add';
|
||||
@@ -916,6 +926,8 @@ class Api {
|
||||
|
||||
static const String spaceAudio = '/audio/music-service/web/song/upper';
|
||||
|
||||
static const String spaceCheese = '/pugv/app/web/season/page';
|
||||
|
||||
static const String dynMention = '/x/polymer/web-dynamic/v1/mention/search';
|
||||
|
||||
static const String createVote = '/x/vote/create';
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_note/list.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/data.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_topic/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_fav/data.dart';
|
||||
import 'package:PiliPlus/models_new/sub/sub_detail/data.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
@@ -142,6 +143,58 @@ class FavHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState<SpaceCheeseData>> favPugv({
|
||||
required int mid,
|
||||
required int page,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.favPugv,
|
||||
queryParameters: {
|
||||
'mid': mid,
|
||||
'ps': 20,
|
||||
'pn': page,
|
||||
'web_location': 333.1387,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(SpaceCheeseData.fromJson(res.data['data']));
|
||||
} else {
|
||||
return Error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future addFavPugv(seasonId) async {
|
||||
var res = await Request().post(
|
||||
Api.addFavPugv,
|
||||
data: {
|
||||
'season_id': seasonId,
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
static Future delFavPugv(seasonId) async {
|
||||
var res = await Request().post(
|
||||
Api.delFavPugv,
|
||||
data: {
|
||||
'season_id': seasonId,
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState<FavTopicData>> favTopic({
|
||||
required int page,
|
||||
}) async {
|
||||
@@ -312,12 +365,12 @@ class FavHttp {
|
||||
|
||||
static Future delNote({
|
||||
required bool isPublish,
|
||||
required List noteIds,
|
||||
required String noteIds,
|
||||
}) async {
|
||||
final res = await Request().post(
|
||||
isPublish ? Api.delPublishNote : Api.delNote,
|
||||
data: {
|
||||
isPublish ? 'cvids' : 'note_ids': noteIds.join(','),
|
||||
isPublish ? 'cvids' : 'note_ids': noteIds,
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(
|
||||
@@ -375,10 +428,10 @@ class FavHttp {
|
||||
}
|
||||
|
||||
static Future sortFavFolder({
|
||||
required List<int?> sort,
|
||||
required String sort,
|
||||
}) async {
|
||||
Map<String, dynamic> data = {
|
||||
'sort': sort.join(','),
|
||||
'sort': sort,
|
||||
'csrf': Accounts.main.csrf,
|
||||
};
|
||||
AppSign.appSign(data);
|
||||
@@ -398,11 +451,11 @@ class FavHttp {
|
||||
|
||||
static Future sortFav({
|
||||
required dynamic mediaId,
|
||||
required List<String> sort,
|
||||
required String sort,
|
||||
}) async {
|
||||
Map<String, dynamic> data = {
|
||||
'media_id': mediaId,
|
||||
'sort': sort.join(','),
|
||||
'sort': sort,
|
||||
'csrf': Accounts.main.csrf,
|
||||
};
|
||||
AppSign.appSign(data);
|
||||
@@ -442,12 +495,12 @@ class FavHttp {
|
||||
}
|
||||
|
||||
static Future deleteFolder({
|
||||
required List<dynamic> mediaIds,
|
||||
required String mediaIds,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
Api.deleteFolder,
|
||||
data: {
|
||||
'media_ids': mediaIds.join(','),
|
||||
'media_ids': mediaIds,
|
||||
'platform': 'web',
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
@@ -537,19 +590,20 @@ class FavHttp {
|
||||
static Future<LoadingState<List<SpaceFavData>?>> spaceFav({
|
||||
required int mid,
|
||||
}) async {
|
||||
Map<String, String> data = {
|
||||
'build': '8430300',
|
||||
final params = {
|
||||
'build': 8430300,
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'bili',
|
||||
'channel': 'master',
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
'up_mid': mid.toString(),
|
||||
'up_mid': mid,
|
||||
};
|
||||
var res = await Request().get(
|
||||
Api.spaceFav,
|
||||
queryParameters: data,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'bili-http-engine': 'cronet',
|
||||
@@ -636,13 +690,13 @@ class FavHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future copyOrMoveFav({
|
||||
static Future<LoadingState> copyOrMoveFav({
|
||||
required bool isCopy,
|
||||
required bool isFav,
|
||||
required dynamic srcMediaId,
|
||||
required dynamic tarMediaId,
|
||||
dynamic mid,
|
||||
required List resources,
|
||||
required String resources,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
isFav
|
||||
@@ -656,28 +710,28 @@ class FavHttp {
|
||||
'src_media_id': ?srcMediaId,
|
||||
'tar_media_id': tarMediaId,
|
||||
'mid': ?mid,
|
||||
'resources': resources.join(','),
|
||||
'resources': resources,
|
||||
'platform': 'web',
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
return const Success(null);
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
return Error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future allFavFolders(mid) async {
|
||||
static Future<LoadingState<FavFolderData>> allFavFolders(Object mid) async {
|
||||
var res = await Request().get(
|
||||
Api.favFolder,
|
||||
queryParameters: {'up_mid': mid},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': FavFolderData.fromJson(res.data['data'])};
|
||||
return Success(FavFolderData.fromJson(res.data['data']));
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
return Error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/login.dart';
|
||||
import 'package:PiliPlus/http/ua_type.dart';
|
||||
import 'package:PiliPlus/models/common/live_search_type.dart';
|
||||
import 'package:PiliPlus/models_new/live/live_area_list/area_item.dart';
|
||||
@@ -169,23 +170,25 @@ class LiveHttp {
|
||||
final params = {
|
||||
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'channel': 'master',
|
||||
'actionKey': 'appkey',
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'device': 'android',
|
||||
'device_name': 'android',
|
||||
'device_type': '0',
|
||||
'fnval': '912',
|
||||
'disable_rcmd': '0',
|
||||
'https_url_req': '1',
|
||||
if (moduleSelect == true) 'module_select': '1',
|
||||
'mobi_app': 'android_hd',
|
||||
'device_type': 0,
|
||||
'fnval': 912,
|
||||
'disable_rcmd': 0,
|
||||
'https_url_req': 1,
|
||||
if (moduleSelect == true) 'module_select': 1,
|
||||
'mobi_app': 'android',
|
||||
'network': 'wifi',
|
||||
'page': pn,
|
||||
'platform': 'android',
|
||||
if (isLogin) 'relation_page': '1',
|
||||
if (isLogin) 'relation_page': 1,
|
||||
's_locale': 'zh_CN',
|
||||
'scale': '2',
|
||||
'scale': 2,
|
||||
'statistics': Constants.statisticsApp,
|
||||
'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
};
|
||||
@@ -197,6 +200,23 @@ class LiveHttp {
|
||||
var res = await Request().get(
|
||||
Api.liveFeedIndex,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'buvid': LoginHttp.buvid,
|
||||
'fp_local':
|
||||
'1111111111111111111111111111111111111111111111111111111111111111',
|
||||
'fp_remote':
|
||||
'1111111111111111111111111111111111111111111111111111111111111111',
|
||||
'session_id': '11111111',
|
||||
'env': 'prod',
|
||||
'app-key': 'android',
|
||||
'User-Agent': Constants.userAgentApp,
|
||||
'x-bili-trace-id': Constants.traceId,
|
||||
'x-bili-aurora-eid': '',
|
||||
'x-bili-aurora-zone': '',
|
||||
'bili-http-engine': 'cronet',
|
||||
},
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(LiveIndexData.fromJson(res.data['data']));
|
||||
@@ -233,29 +253,31 @@ class LiveHttp {
|
||||
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'actionKey': 'appkey',
|
||||
'channel': 'master',
|
||||
'area_id': ?areaId,
|
||||
'parent_area_id': ?parentAreaId,
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'device': 'android',
|
||||
'device_name': 'android',
|
||||
'device_type': '0',
|
||||
'fnval': '912',
|
||||
'disable_rcmd': '0',
|
||||
'https_url_req': '1',
|
||||
'mobi_app': 'android_hd',
|
||||
'module_select': '0',
|
||||
'device_type': 0,
|
||||
'fnval': 912,
|
||||
'disable_rcmd': 0,
|
||||
'https_url_req': 1,
|
||||
'mobi_app': 'android',
|
||||
'module_select': 0,
|
||||
'network': 'wifi',
|
||||
'page': pn,
|
||||
'page_size': '20',
|
||||
'page_size': 20,
|
||||
'platform': 'android',
|
||||
'qn': '0',
|
||||
'qn': 0,
|
||||
'sort_type': ?sortType,
|
||||
'tag_version': '1',
|
||||
'tag_version': 1,
|
||||
's_locale': 'zh_CN',
|
||||
'scale': '2',
|
||||
'scale': 2,
|
||||
'statistics': Constants.statisticsApp,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
};
|
||||
AppSign.appSign(
|
||||
params,
|
||||
@@ -265,6 +287,23 @@ class LiveHttp {
|
||||
var res = await Request().get(
|
||||
Api.liveSecondList,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'buvid': LoginHttp.buvid,
|
||||
'fp_local':
|
||||
'1111111111111111111111111111111111111111111111111111111111111111',
|
||||
'fp_remote':
|
||||
'1111111111111111111111111111111111111111111111111111111111111111',
|
||||
'session_id': '11111111',
|
||||
'env': 'prod',
|
||||
'app-key': 'android',
|
||||
'User-Agent': Constants.userAgentApp,
|
||||
'x-bili-trace-id': Constants.traceId,
|
||||
'x-bili-aurora-eid': '',
|
||||
'x-bili-aurora-zone': '',
|
||||
'bili-http-engine': 'cronet',
|
||||
},
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(LiveSecondData.fromJson(res.data['data']));
|
||||
@@ -280,11 +319,13 @@ class LiveHttp {
|
||||
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'actionKey': 'appkey',
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'channel': 'master',
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'disable_rcmd': '0',
|
||||
'mobi_app': 'android_hd',
|
||||
'device': 'android',
|
||||
'disable_rcmd': 0,
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
@@ -317,11 +358,13 @@ class LiveHttp {
|
||||
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'actionKey': 'appkey',
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'channel': 'master',
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'disable_rcmd': '0',
|
||||
'mobi_app': 'android_hd',
|
||||
'device': 'android',
|
||||
'disable_rcmd': 0,
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
@@ -350,18 +393,20 @@ class LiveHttp {
|
||||
}
|
||||
|
||||
static Future setLiveFavTag({
|
||||
required List ids,
|
||||
required String ids,
|
||||
}) async {
|
||||
final data = {
|
||||
'tags': ids.join(','),
|
||||
'tags': ids,
|
||||
'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'actionKey': 'appkey',
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'channel': 'master',
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'disable_rcmd': '0',
|
||||
'mobi_app': 'android_hd',
|
||||
'device': 'android',
|
||||
'disable_rcmd': 0,
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
@@ -395,14 +440,16 @@ class LiveHttp {
|
||||
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'actionKey': 'appkey',
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'channel': 'master',
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'disable_rcmd': '0',
|
||||
'device': 'android',
|
||||
'disable_rcmd': 0,
|
||||
'need_entrance': 1,
|
||||
'parent_id': parentid,
|
||||
'source_id': 2,
|
||||
'mobi_app': 'android_hd',
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
@@ -436,14 +483,16 @@ class LiveHttp {
|
||||
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
'actionKey': 'appkey',
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'channel': 'master',
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'device': 'pad',
|
||||
'device': 'android',
|
||||
'page': page,
|
||||
'pagesize': 30,
|
||||
'keyword': keyword,
|
||||
'disable_rcmd': '0',
|
||||
'mobi_app': 'android_hd',
|
||||
'disable_rcmd': 0,
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
|
||||
@@ -20,6 +20,7 @@ import 'package:PiliPlus/models_new/space/space/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_archive/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_article/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_audio/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_opus/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_season_series/item.dart';
|
||||
import 'package:PiliPlus/models_new/upower_rank/data.dart';
|
||||
@@ -57,21 +58,22 @@ class MemberHttp {
|
||||
required int mid,
|
||||
required int page,
|
||||
}) async {
|
||||
Map<String, String> data = {
|
||||
'build': '8430300',
|
||||
final params = {
|
||||
'build': 8430300,
|
||||
'channel': 'master',
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'bili',
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
'pn': page.toString(),
|
||||
'ps': '10',
|
||||
'pn': page,
|
||||
'ps': 10,
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
'vmid': mid.toString(),
|
||||
'vmid': mid,
|
||||
};
|
||||
var res = await Request().get(
|
||||
Api.spaceArticle,
|
||||
queryParameters: data,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'bili-http-engine': 'cronet',
|
||||
@@ -119,25 +121,26 @@ class MemberHttp {
|
||||
int? seriesId,
|
||||
includeCursor,
|
||||
}) async {
|
||||
Map<String, String> data = {
|
||||
if (aid != null) 'aid': aid.toString(),
|
||||
'build': '8430300',
|
||||
final params = {
|
||||
'aid': ?aid,
|
||||
'build': 8430300,
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'bili',
|
||||
'channel': 'master',
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'ps': '20',
|
||||
if (pn != null) 'pn': pn.toString(),
|
||||
if (next != null) 'next': next.toString(),
|
||||
if (seasonId != null) 'season_id': seasonId.toString(),
|
||||
if (seriesId != null) 'series_id': seriesId.toString(),
|
||||
'qn': type == ContributeType.video ? '80' : '32',
|
||||
'ps': 20,
|
||||
'pn': ?pn,
|
||||
'next': ?next,
|
||||
'season_id': ?seasonId,
|
||||
'series_id': ?seriesId,
|
||||
'qn': type == ContributeType.video ? 80 : 32,
|
||||
'order': ?order,
|
||||
'sort': ?sort,
|
||||
if (includeCursor != null) 'include_cursor': includeCursor.toString(),
|
||||
'include_cursor': ?includeCursor,
|
||||
'statistics': Constants.statisticsApp,
|
||||
'vmid': mid.toString(),
|
||||
'vmid': mid,
|
||||
};
|
||||
var res = await Request().get(
|
||||
switch (type) {
|
||||
@@ -148,7 +151,7 @@ class MemberHttp {
|
||||
ContributeType.bangumi => Api.spaceBangumi,
|
||||
ContributeType.comic => Api.spaceComic,
|
||||
},
|
||||
queryParameters: data,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'bili-http-engine': 'cronet',
|
||||
@@ -184,6 +187,26 @@ class MemberHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState<SpaceCheeseData>> spaceCheese({
|
||||
required int page,
|
||||
required mid,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.spaceCheese,
|
||||
queryParameters: {
|
||||
'pn': page,
|
||||
'ps': 30,
|
||||
'mid': mid,
|
||||
'web_location': 333.1387,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(SpaceCheeseData.fromJson(res.data['data']));
|
||||
} else {
|
||||
return Error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> spaceStory({
|
||||
required mid,
|
||||
required aid,
|
||||
@@ -193,25 +216,26 @@ class MemberHttp {
|
||||
required contain,
|
||||
required index,
|
||||
}) async {
|
||||
Map<String, String> data = {
|
||||
'aid': aid.toString(),
|
||||
'before_size': beforeSize.toString(),
|
||||
'after_size': afterSize.toString(),
|
||||
'cid': cid.toString(),
|
||||
'contain': contain.toString(),
|
||||
'index': index.toString(),
|
||||
'build': '8430300',
|
||||
final params = {
|
||||
'aid': aid,
|
||||
'before_size': beforeSize,
|
||||
'after_size': afterSize,
|
||||
'cid': cid,
|
||||
'contain': contain,
|
||||
'index': index,
|
||||
'build': 8430300,
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'bili',
|
||||
'channel': 'master',
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statisticsApp,
|
||||
'vmid': mid.toString(),
|
||||
'vmid': mid,
|
||||
};
|
||||
var res = await Request().get(
|
||||
Api.spaceStory,
|
||||
queryParameters: data,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'bili-http-engine': 'cronet',
|
||||
@@ -230,20 +254,21 @@ class MemberHttp {
|
||||
int? mid,
|
||||
dynamic fromViewAid,
|
||||
}) async {
|
||||
Map<String, String> data = {
|
||||
'build': '8430300',
|
||||
final params = {
|
||||
'build': 8430300,
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'bili',
|
||||
'channel': 'master',
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'from_view_aid': ?fromViewAid,
|
||||
'statistics': Constants.statisticsApp,
|
||||
'vmid': mid.toString(),
|
||||
'vmid': mid,
|
||||
};
|
||||
var res = await Request().get(
|
||||
Api.space,
|
||||
queryParameters: data,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'bili-http-engine': 'cronet',
|
||||
@@ -341,7 +366,7 @@ class MemberHttp {
|
||||
'keyword': ?keyword,
|
||||
'order': order,
|
||||
'platform': 'web',
|
||||
'web_location': '1550101',
|
||||
'web_location': 1550101,
|
||||
'order_avoided': orderAvoided,
|
||||
'dm_img_list': '[]',
|
||||
'dm_img_str': dmImgStr,
|
||||
@@ -462,17 +487,16 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
// 查询分组
|
||||
static Future followUpTags() async {
|
||||
static Future<LoadingState<List<MemberTagItemModel>>> followUpTags() async {
|
||||
var res = await Request().get(Api.followUpTag);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data']
|
||||
.map<MemberTagItemModel>((e) => MemberTagItemModel.fromJson(e))
|
||||
return Success(
|
||||
(res.data['data'] as List)
|
||||
.map((e) => MemberTagItemModel.fromJson(e))
|
||||
.toList(),
|
||||
};
|
||||
);
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
return Error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +525,7 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
// 设置分组
|
||||
static Future addUsers(List<int?> fids, List<int?> tagids) async {
|
||||
static Future addUsers(String fids, String tagids) async {
|
||||
var res = await Request().post(
|
||||
Api.addUsers,
|
||||
queryParameters: {
|
||||
@@ -509,8 +533,8 @@ class MemberHttp {
|
||||
'{"platform":"web","device":"pc","spmid":"333.1387"}',
|
||||
},
|
||||
data: {
|
||||
'fids': fids.join(','),
|
||||
'tagids': tagids.join(','),
|
||||
'fids': fids,
|
||||
'tagids': tagids,
|
||||
'csrf': Accounts.main.csrf,
|
||||
// 'cross_domain': true
|
||||
},
|
||||
|
||||
@@ -533,13 +533,13 @@ class MsgHttp {
|
||||
}
|
||||
|
||||
static Future<LoadingState<List<ImUserInfosData>?>> imUserInfos({
|
||||
required List uids,
|
||||
required String uids,
|
||||
}) async {
|
||||
final csrf = Accounts.main.csrf;
|
||||
var res = await Request().get(
|
||||
Api.imUserInfos,
|
||||
queryParameters: {
|
||||
'uids': uids.join(','),
|
||||
'uids': uids,
|
||||
'build': 0,
|
||||
'mobi_app': 'web',
|
||||
'csrf_token': csrf,
|
||||
|
||||
@@ -193,6 +193,24 @@ class SearchHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState<PgcInfoModel>> pugvInfo({
|
||||
dynamic seasonId,
|
||||
dynamic epId,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.pugvInfo,
|
||||
queryParameters: {
|
||||
'season_id': ?seasonId,
|
||||
'ep_id': ?epId,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(PgcInfoModel.fromJson(res.data['data']));
|
||||
} else {
|
||||
return Error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> episodeInfo({dynamic epId}) async {
|
||||
var res = await Request().get(
|
||||
Api.episodeInfo,
|
||||
@@ -227,7 +245,9 @@ class SearchHttp {
|
||||
final res = await Request().get(
|
||||
Api.searchRecommend,
|
||||
queryParameters: {
|
||||
'build': '8430300',
|
||||
'build': 8430300,
|
||||
'channel': 'master',
|
||||
'version': '8.43.0',
|
||||
'c_locale': 'zh_CN',
|
||||
'mobi_app': 'android',
|
||||
'platform': 'android',
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:PiliPlus/models_new/space_setting/data.dart';
|
||||
import 'package:PiliPlus/models_new/sub/sub/data.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_tag/data.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/global_data.dart';
|
||||
import 'package:PiliPlus/utils/wbi_sign.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
@@ -80,6 +81,7 @@ class UserHttp {
|
||||
required String type,
|
||||
int? max,
|
||||
int? viewAt,
|
||||
Account? account,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.historyList,
|
||||
@@ -89,6 +91,7 @@ class UserHttp {
|
||||
'max': max ?? 0,
|
||||
'view_at': viewAt ?? 0,
|
||||
},
|
||||
options: Options(extra: {'account': account ?? Accounts.history}),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(HistoryData.fromJson(res.data['data']));
|
||||
@@ -98,22 +101,27 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 暂停观看历史
|
||||
static Future pauseHistory(bool switchStatus) async {
|
||||
static Future pauseHistory(bool switchStatus, {Account? account}) async {
|
||||
// 暂停switchStatus传true 否则false
|
||||
account ??= Accounts.history;
|
||||
var res = await Request().post(
|
||||
Api.pauseHistory,
|
||||
queryParameters: {
|
||||
'switch': switchStatus,
|
||||
'jsonp': 'jsonp',
|
||||
'csrf': Accounts.main.csrf,
|
||||
'csrf': account.csrf,
|
||||
},
|
||||
options: Options(extra: {'account': account}),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
// 观看历史暂停状态
|
||||
static Future historyStatus() async {
|
||||
var res = await Request().get(Api.historyStatus);
|
||||
static Future historyStatus({Account? account}) async {
|
||||
var res = await Request().get(
|
||||
Api.historyStatus,
|
||||
options: Options(extra: {'account': account ?? Accounts.history}),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
} else {
|
||||
@@ -122,13 +130,15 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 清空历史记录
|
||||
static Future clearHistory() async {
|
||||
static Future clearHistory({Account? account}) async {
|
||||
account ??= Accounts.history;
|
||||
var res = await Request().post(
|
||||
Api.clearHistory,
|
||||
queryParameters: {
|
||||
'jsonp': 'jsonp',
|
||||
'csrf': Accounts.main.csrf,
|
||||
'csrf': account.csrf,
|
||||
},
|
||||
options: Options(extra: {'account': account}),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
@@ -151,10 +161,10 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 移除已观看
|
||||
static Future toViewDel({required List<int?> aids}) async {
|
||||
static Future toViewDel({required String aids}) async {
|
||||
final Map<String, dynamic> params = {
|
||||
'csrf': Accounts.main.csrf,
|
||||
'resources': aids.join(','),
|
||||
'resources': aids,
|
||||
};
|
||||
var res = await Request().post(
|
||||
Api.toViewDel,
|
||||
@@ -204,15 +214,17 @@ class UserHttp {
|
||||
}
|
||||
|
||||
// 删除历史记录
|
||||
static Future delHistory(List<String> kidList) async {
|
||||
static Future delHistory(String kid, {Account? account}) async {
|
||||
account ??= Accounts.history;
|
||||
var res = await Request().post(
|
||||
Api.delHistory,
|
||||
data: {
|
||||
'kid': kidList.join(','),
|
||||
'kid': kid,
|
||||
'jsonp': 'jsonp',
|
||||
'csrf': Accounts.main.csrf,
|
||||
'csrf': account.csrf,
|
||||
},
|
||||
options: Options(
|
||||
extra: {'account': account},
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
),
|
||||
);
|
||||
@@ -241,6 +253,7 @@ class UserHttp {
|
||||
static Future<LoadingState<HistoryData>> searchHistory({
|
||||
required int pn,
|
||||
required String keyword,
|
||||
Account? account,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.searchHistory,
|
||||
@@ -249,6 +262,7 @@ class UserHttp {
|
||||
'keyword': keyword,
|
||||
'business': 'all',
|
||||
},
|
||||
options: Options(extra: {'account': account ?? Accounts.history}),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(HistoryData.fromJson(res.data['data']));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
@@ -8,6 +7,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/login.dart';
|
||||
import 'package:PiliPlus/http/ua_type.dart';
|
||||
import 'package:PiliPlus/models/common/account_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/video_type.dart';
|
||||
import 'package:PiliPlus/models/home/rcmd/result.dart';
|
||||
import 'package:PiliPlus/models/model_hot_video_item.dart';
|
||||
import 'package:PiliPlus/models/model_rec_video_item.dart';
|
||||
@@ -23,6 +23,7 @@ import 'package:PiliPlus/models_new/video/video_note_list/data.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_play_info/data.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_relation/data.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/global_data.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
@@ -34,7 +35,6 @@ import 'package:flutter/foundation.dart';
|
||||
|
||||
/// view层根据 status 判断渲染逻辑
|
||||
class VideoHttp {
|
||||
static bool p1080 = Pref.p1080;
|
||||
// static bool enableRcmdDynamic = Pref.enableRcmdDynamic;
|
||||
static RegExp zoneRegExp = RegExp(Pref.banWordForZone, caseSensitive: false);
|
||||
static bool enableFilter = zoneRegExp.pattern.isNotEmpty;
|
||||
@@ -77,38 +77,38 @@ class VideoHttp {
|
||||
|
||||
// 添加额外的loginState变量模拟未登录状态
|
||||
static Future<LoadingState> rcmdVideoListApp({required int freshIdx}) async {
|
||||
Map<String, String> data = {
|
||||
'build': '2001100',
|
||||
final params = {
|
||||
'build': 2001100,
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'master',
|
||||
'column': '4',
|
||||
'column': 4,
|
||||
'device': 'pad',
|
||||
'device_name': 'android',
|
||||
'device_type': '0',
|
||||
'disable_rcmd': '0',
|
||||
'flush': '5',
|
||||
'fnval': '976',
|
||||
'fnver': '0',
|
||||
'force_host': '2', //使用https
|
||||
'fourk': '1',
|
||||
'guidance': '0',
|
||||
'https_url_req': '0',
|
||||
'idx': freshIdx.toString(),
|
||||
'device_type': 0,
|
||||
'disable_rcmd': 0,
|
||||
'flush': 5,
|
||||
'fnval': 976,
|
||||
'fnver': 0,
|
||||
'force_host': 2, //使用https
|
||||
'fourk': 1,
|
||||
'guidance': 0,
|
||||
'https_url_req': 0,
|
||||
'idx': freshIdx,
|
||||
'mobi_app': 'android_hd',
|
||||
'network': 'wifi',
|
||||
'platform': 'android',
|
||||
'player_net': '1',
|
||||
'player_net': 1,
|
||||
'pull': freshIdx == 0 ? 'true' : 'false',
|
||||
'qn': '32',
|
||||
'recsys_mode': '0',
|
||||
'qn': 32,
|
||||
'recsys_mode': 0,
|
||||
's_locale': 'zh_CN',
|
||||
'splash_id': '',
|
||||
'statistics': Constants.statistics,
|
||||
'voice_balance': '0',
|
||||
'voice_balance': 0,
|
||||
};
|
||||
var res = await Request().get(
|
||||
Api.recommendListApp,
|
||||
queryParameters: data,
|
||||
queryParameters: params,
|
||||
options: Options(
|
||||
headers: {
|
||||
'buvid': LoginHttp.buvid,
|
||||
@@ -194,7 +194,8 @@ class VideoHttp {
|
||||
int? qn,
|
||||
dynamic epid,
|
||||
dynamic seasonId,
|
||||
bool? forcePgcApi,
|
||||
required bool tryLook,
|
||||
required VideoType videoType,
|
||||
}) async {
|
||||
final params = await WbiSign.makSign({
|
||||
'avid': ?avid,
|
||||
@@ -212,33 +213,34 @@ class VideoHttp {
|
||||
'isGaiaAvoided': true,
|
||||
'web_location': 1315873,
|
||||
// 免登录查看1080p
|
||||
if (!Accounts.get(AccountType.video).isLogin && p1080) 'try_look': 1,
|
||||
if (tryLook) 'try_look': 1,
|
||||
});
|
||||
|
||||
late final usePgcApi =
|
||||
forcePgcApi == true || Accounts.get(AccountType.video).isLogin;
|
||||
|
||||
try {
|
||||
var res = await Request().get(
|
||||
epid != null && usePgcApi ? Api.pgcUrl : Api.ugcUrl,
|
||||
videoType.api,
|
||||
queryParameters: params,
|
||||
);
|
||||
|
||||
if (res.data['code'] == 0) {
|
||||
late PlayUrlModel data;
|
||||
if (epid != null && usePgcApi) {
|
||||
data = PlayUrlModel.fromJson(res.data['result']['video_info'])
|
||||
..lastPlayTime = res
|
||||
.data['result']['play_view_business_info']['user_status']['watch_progress']['current_watch_progress'];
|
||||
} else {
|
||||
data = PlayUrlModel.fromJson(res.data['data']);
|
||||
switch (videoType) {
|
||||
case VideoType.ugc:
|
||||
data = PlayUrlModel.fromJson(res.data['data']);
|
||||
case VideoType.pugv:
|
||||
var result = res.data['data'];
|
||||
data = PlayUrlModel.fromJson(result)
|
||||
..lastPlayTime =
|
||||
result?['play_view_business_info']?['user_status']?['watch_progress']?['current_watch_progress'];
|
||||
case VideoType.pgc:
|
||||
var result = res.data['result'];
|
||||
data = PlayUrlModel.fromJson(result['video_info'])
|
||||
..lastPlayTime =
|
||||
result?['play_view_business_info']?['user_status']?['watch_progress']?['current_watch_progress'];
|
||||
}
|
||||
return {
|
||||
'status': true,
|
||||
'data': data,
|
||||
};
|
||||
return {'status': true, 'data': data};
|
||||
} else {
|
||||
if (epid != null && !usePgcApi && forcePgcApi != true) {
|
||||
if (epid != null && videoType == VideoType.ugc) {
|
||||
return videoUrl(
|
||||
avid: avid,
|
||||
bvid: bvid,
|
||||
@@ -246,7 +248,8 @@ class VideoHttp {
|
||||
qn: qn,
|
||||
epid: epid,
|
||||
seasonId: seasonId,
|
||||
forcePgcApi: true,
|
||||
tryLook: tryLook,
|
||||
videoType: VideoType.pgc,
|
||||
);
|
||||
}
|
||||
return {
|
||||
@@ -465,7 +468,7 @@ class VideoHttp {
|
||||
'id': id,
|
||||
'reason_id': ?reasonId,
|
||||
'feedback_id': ?feedbackId,
|
||||
'build': '1',
|
||||
'build': 1,
|
||||
'mobi_app': 'android',
|
||||
},
|
||||
);
|
||||
@@ -493,7 +496,7 @@ class VideoHttp {
|
||||
'id': id,
|
||||
'reason_id': ?reasonId,
|
||||
'feedback_id': ?feedbackId,
|
||||
'build': '1',
|
||||
'build': 1,
|
||||
'mobi_app': 'android',
|
||||
},
|
||||
);
|
||||
@@ -542,7 +545,6 @@ class VideoHttp {
|
||||
data: data,
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
log(res.toString());
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
} else {
|
||||
@@ -564,7 +566,6 @@ class VideoHttp {
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
);
|
||||
log(res.toString());
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
} else {
|
||||
@@ -620,7 +621,7 @@ class VideoHttp {
|
||||
await Request().post(
|
||||
Api.roomEntryAction,
|
||||
queryParameters: {
|
||||
'csrf': Accounts.main.csrf,
|
||||
'csrf': Accounts.heartbeat.csrf,
|
||||
},
|
||||
data: {
|
||||
'room_id': roomId,
|
||||
@@ -638,31 +639,34 @@ class VideoHttp {
|
||||
queryParameters: {
|
||||
'aid': ?aid,
|
||||
'type': ?type,
|
||||
'csrf': Accounts.main.csrf,
|
||||
'csrf': Accounts.heartbeat.csrf,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 视频播放进度
|
||||
static Future heartBeat({
|
||||
aid,
|
||||
bvid,
|
||||
cid,
|
||||
progress,
|
||||
epid,
|
||||
seasonId,
|
||||
subType,
|
||||
required VideoType videoType,
|
||||
}) async {
|
||||
final isPugv = videoType == VideoType.pugv;
|
||||
await Request().post(
|
||||
Api.heartBeat,
|
||||
queryParameters: {
|
||||
'bvid': bvid,
|
||||
if (isPugv) 'aid': ?aid else 'bvid': ?bvid,
|
||||
'cid': cid,
|
||||
'epid': ?epid,
|
||||
'sid': ?seasonId,
|
||||
if (epid != null) 'type': 4,
|
||||
'type': videoType.type,
|
||||
'sub_type': ?subType,
|
||||
'played_time': progress,
|
||||
'csrf': Accounts.main.csrf,
|
||||
'csrf': Accounts.heartbeat.csrf,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -671,15 +675,18 @@ class VideoHttp {
|
||||
required int desc,
|
||||
required dynamic oid,
|
||||
required dynamic upperMid,
|
||||
Account? account,
|
||||
}) async {
|
||||
account ??= Accounts.history;
|
||||
await Request().post(
|
||||
Api.mediaListHistory,
|
||||
queryParameters: {
|
||||
'desc': desc,
|
||||
'oid': oid,
|
||||
'upper_mid': upperMid,
|
||||
'csrf': Accounts.main.csrf,
|
||||
'csrf': account.csrf,
|
||||
},
|
||||
options: Options(extra: {'account': account}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -736,13 +743,13 @@ class VideoHttp {
|
||||
}
|
||||
|
||||
static Future pgcUpdate({
|
||||
required List seasonId,
|
||||
required dynamic status,
|
||||
required String seasonId,
|
||||
required int status,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
Api.pgcUpdate,
|
||||
data: {
|
||||
'season_id': seasonId.join(','),
|
||||
'season_id': seasonId,
|
||||
'status': status,
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
|
||||
@@ -12,8 +12,8 @@ import 'package:PiliPlus/services/loggeer.dart';
|
||||
import 'package:PiliPlus/services/service_locator.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/cache_manage.dart';
|
||||
import 'package:PiliPlus/utils/data.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
@@ -124,7 +124,7 @@ Commit Hash: ${BuildConfig.commitHash}''';
|
||||
systemNavigationBarContrastEnforced: false,
|
||||
),
|
||||
);
|
||||
Data.init();
|
||||
RequestUtils.syncHistoryStatus();
|
||||
PiliScheme.init();
|
||||
}
|
||||
|
||||
@@ -145,15 +145,14 @@ class MyApp extends StatelessWidget {
|
||||
FlutterDisplayMode.supported.then((value) {
|
||||
modes = value;
|
||||
var storageDisplay = GStorage.setting.get(SettingBoxKey.displayMode);
|
||||
DisplayMode f = DisplayMode.auto;
|
||||
DisplayMode? displayMode;
|
||||
if (storageDisplay != null) {
|
||||
f = modes.firstWhere(
|
||||
displayMode = modes.firstWhereOrNull(
|
||||
(e) => e.toString() == storageDisplay,
|
||||
orElse: () => f,
|
||||
);
|
||||
}
|
||||
DisplayMode preferred = modes.toList().firstWhere((el) => el == f);
|
||||
FlutterDisplayMode.setPreferredMode(preferred);
|
||||
displayMode ??= DisplayMode.auto;
|
||||
FlutterDisplayMode.setPreferredMode(displayMode);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
enum EpisodeType {
|
||||
part('分P'),
|
||||
season('合集'),
|
||||
pgc('番剧');
|
||||
pgc('剧集');
|
||||
|
||||
final String title;
|
||||
const EpisodeType(this.title);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/pages/fav/article/view.dart';
|
||||
import 'package:PiliPlus/pages/fav/cheese/view.dart';
|
||||
import 'package:PiliPlus/pages/fav/note/view.dart';
|
||||
import 'package:PiliPlus/pages/fav/pgc/view.dart';
|
||||
import 'package:PiliPlus/pages/fav/topic/view.dart';
|
||||
@@ -11,7 +12,8 @@ enum FavTabType {
|
||||
cinema('追剧', FavPgcPage(type: 2)),
|
||||
article('专栏', FavArticlePage()),
|
||||
note('笔记', FavNotePage()),
|
||||
topic('话题', FavTopicPage());
|
||||
topic('话题', FavTopicPage()),
|
||||
cheese('课堂', FavCheesePage());
|
||||
|
||||
final String title;
|
||||
final Widget page;
|
||||
|
||||
@@ -10,11 +10,6 @@ enum HistoryBusinessType {
|
||||
// 文章
|
||||
article('article');
|
||||
|
||||
// 隐藏时长
|
||||
static const hiddenDurationType = {'live', 'article-list', 'article'};
|
||||
// 右上
|
||||
static const showBadge = {'pgc', 'article-list', 'article'};
|
||||
|
||||
final String type;
|
||||
const HistoryBusinessType(this.type);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,17 @@ enum MemberTabType {
|
||||
dynamic('动态'),
|
||||
contribute('投稿'),
|
||||
favorite('收藏'),
|
||||
bangumi('番剧');
|
||||
bangumi('番剧'),
|
||||
cheese('课堂');
|
||||
|
||||
static bool contains(String type) {
|
||||
for (var e in MemberTabType.values) {
|
||||
if (e.name == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final String title;
|
||||
const MemberTabType(this.title);
|
||||
|
||||
27
lib/models/common/search/article_search_type.dart
Normal file
27
lib/models/common/search/article_search_type.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
enum ArticleOrderType {
|
||||
totalrank('综合排序'),
|
||||
pubdate('最新发布'),
|
||||
click('最多点击'),
|
||||
attention('最多喜欢'),
|
||||
scores('最多评论');
|
||||
|
||||
String get order => name;
|
||||
final String label;
|
||||
const ArticleOrderType(this.label);
|
||||
}
|
||||
|
||||
enum ArticleZoneType {
|
||||
all('全部分区', 0),
|
||||
douga('动画', 2),
|
||||
game('游戏', 1),
|
||||
cinephile('影视', 28),
|
||||
life('生活', 3),
|
||||
interest('兴趣', 29),
|
||||
novel('轻小说', 16),
|
||||
tech('科技', 17),
|
||||
note('笔记', 41);
|
||||
|
||||
final String label;
|
||||
final int categoryId;
|
||||
const ArticleZoneType(this.label, this.categoryId);
|
||||
}
|
||||
22
lib/models/common/search/user_search_type.dart
Normal file
22
lib/models/common/search/user_search_type.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
enum UserOrderType {
|
||||
def('默认排序', 0, ''),
|
||||
fansDesc('粉丝数由高到低', 0, 'fans'),
|
||||
fansAsc('粉丝数由低到高', 1, 'fans'),
|
||||
levelDesc('Lv等级由高到低', 0, 'level'),
|
||||
levelAsc('Lv等级由低到高', 1, 'level');
|
||||
|
||||
final String label;
|
||||
final int orderSort;
|
||||
final String order;
|
||||
const UserOrderType(this.label, this.orderSort, this.order);
|
||||
}
|
||||
|
||||
enum UserType {
|
||||
all('全部用户'),
|
||||
up('UP主'),
|
||||
common('普通用户'),
|
||||
verified('认证用户');
|
||||
|
||||
final String label;
|
||||
const UserType(this.label);
|
||||
}
|
||||
49
lib/models/common/search/video_search_type.dart
Normal file
49
lib/models/common/search/video_search_type.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
enum VideoPubTimeType {
|
||||
all('不限'),
|
||||
day('最近一天'),
|
||||
week('最近一周'),
|
||||
halfYear('最近半年');
|
||||
|
||||
final String label;
|
||||
const VideoPubTimeType(this.label);
|
||||
}
|
||||
|
||||
enum VideoDurationType {
|
||||
all('全部时长'),
|
||||
tenMins('0-10分钟'),
|
||||
halfHour('0-30分钟'),
|
||||
hour('30-60分钟'),
|
||||
hourPlus('60分钟+');
|
||||
|
||||
final String label;
|
||||
const VideoDurationType(this.label);
|
||||
}
|
||||
|
||||
enum VideoZoneType {
|
||||
all('全部'),
|
||||
douga('动画', tids: 1),
|
||||
anime('番剧', tids: 13),
|
||||
guochuang('国创', tids: 167),
|
||||
music('音乐', tids: 3),
|
||||
dance('舞蹈', tids: 129),
|
||||
game('游戏', tids: 4),
|
||||
knowledge('知识', tids: 36),
|
||||
tech('科技', tids: 188),
|
||||
sports('运动', tids: 234),
|
||||
car('汽车', tids: 223),
|
||||
life('生活', tids: 160),
|
||||
food('美食', tids: 221),
|
||||
animal('动物', tids: 217),
|
||||
kichiku('鬼畜', tids: 119),
|
||||
fashion('时尚', tids: 115),
|
||||
info('资讯', tids: 202),
|
||||
ent('娱乐', tids: 5),
|
||||
cinephile('影视', tids: 181),
|
||||
documentary('记录', tids: 177),
|
||||
movie('电影', tids: 23),
|
||||
tv('电视', tids: 11);
|
||||
|
||||
final String label;
|
||||
final int? tids;
|
||||
const VideoZoneType(this.label, {this.tids});
|
||||
}
|
||||
26
lib/models/common/video/video_type.dart
Normal file
26
lib/models/common/video/video_type.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
|
||||
enum VideoType {
|
||||
ugc(
|
||||
type: 3,
|
||||
api: Api.ugcUrl,
|
||||
),
|
||||
pgc(
|
||||
type: 4,
|
||||
api: Api.pgcUrl,
|
||||
),
|
||||
pugv(
|
||||
type: 10,
|
||||
replyType: 33,
|
||||
api: Api.pugvUrl,
|
||||
);
|
||||
|
||||
final int type;
|
||||
final String api;
|
||||
final int replyType;
|
||||
const VideoType({
|
||||
required this.api,
|
||||
required this.type,
|
||||
this.replyType = 1,
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
|
||||
class MemberTagItemModel with MultiSelectData {
|
||||
class MemberTagItemModel {
|
||||
MemberTagItemModel({
|
||||
this.count,
|
||||
this.name,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:PiliPlus/models/model_owner.dart';
|
||||
import 'package:PiliPlus/models/model_rec_video_item.dart';
|
||||
import 'package:PiliPlus/models/model_video.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
// 稍后再看, 排行榜等网页返回也使用该类
|
||||
class HotVideoItemModel extends BaseRecVideoItemModel with MultiSelectData {
|
||||
|
||||
@@ -170,23 +170,25 @@ abstract class BaseItem {
|
||||
|
||||
BaseItem.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
baseUrl = json['baseUrl'];
|
||||
final backupUrls = (json['backupUrl'] as List?)?.cast<String>() ?? [];
|
||||
baseUrl = json['baseUrl'] ?? json['base_url'];
|
||||
final backupUrls =
|
||||
((json['backupUrl'] ?? json['backup_url']) as List?)?.cast<String>() ??
|
||||
<String>[];
|
||||
backupUrl = backupUrls.isNotEmpty
|
||||
? backupUrls.firstWhere(
|
||||
(i) => !_isMCDNorPCDN(i),
|
||||
orElse: () => backupUrls.first,
|
||||
)
|
||||
: null;
|
||||
bandWidth = json['bandWidth'];
|
||||
bandWidth = json['bandWidth'] ?? json['bandwidth'];
|
||||
mimeType = json['mime_type'];
|
||||
codecs = json['codecs'];
|
||||
width = json['width'];
|
||||
height = json['height'];
|
||||
frameRate = json['frameRate'];
|
||||
frameRate = json['frameRate'] ?? json['frame_rate'];
|
||||
sar = json['sar'];
|
||||
startWithSap = json['startWithSap'];
|
||||
segmentBase = json['segmentBase'];
|
||||
startWithSap = json['startWithSap'] ?? json['start_with_sap'];
|
||||
segmentBase = json['segmentBase'] ?? json['segment_base'];
|
||||
codecid = json['codecid'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
class MentionItem with MultiSelectData {
|
||||
String? face;
|
||||
|
||||
@@ -2,8 +2,7 @@ import 'package:PiliPlus/models/model_owner.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/cnt_info.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/ogv.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/ugc.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
class FavDetailItemModel with MultiSelectData {
|
||||
int? id;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
class FavNoteItemModel with MultiSelectData {
|
||||
FavNoteItemModel({
|
||||
|
||||
@@ -11,8 +11,7 @@ import 'package:PiliPlus/models_new/fav/fav_pgc/rights.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/section.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/series.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/stat.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
class FavPgcItemModel with MultiSelectData {
|
||||
int? seasonId;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:PiliPlus/models_new/history/history.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
class HistoryItemModel with MultiSelectData {
|
||||
String? title;
|
||||
|
||||
@@ -4,8 +4,7 @@ import 'package:PiliPlus/models_new/later/owner.dart';
|
||||
import 'package:PiliPlus/models_new/later/page.dart';
|
||||
import 'package:PiliPlus/models_new/later/rights.dart';
|
||||
import 'package:PiliPlus/models_new/later/stat.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart'
|
||||
show MultiSelectData;
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
class LaterItemModel with MultiSelectData {
|
||||
int? aid;
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import 'package:PiliPlus/models_new/media_list/media_list.dart';
|
||||
|
||||
class MediaListData {
|
||||
List<MediaListItemModel>? mediaList;
|
||||
List<MediaListItemModel> mediaList;
|
||||
bool? hasMore;
|
||||
int? totalCount;
|
||||
String? nextStartKey;
|
||||
|
||||
MediaListData({
|
||||
this.mediaList,
|
||||
required this.mediaList,
|
||||
this.hasMore,
|
||||
this.totalCount,
|
||||
this.nextStartKey,
|
||||
});
|
||||
|
||||
factory MediaListData.fromJson(Map<String, dynamic> json) => MediaListData(
|
||||
mediaList: (json['media_list'] as List<dynamic>?)
|
||||
?.map((e) => MediaListItemModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
mediaList:
|
||||
(json['media_list'] as List<dynamic>?)
|
||||
?.map((e) => MediaListItemModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
<MediaListItemModel>[],
|
||||
hasMore: json['has_more'] as bool?,
|
||||
totalCount: json['total_count'] as int?,
|
||||
nextStartKey: json['next_start_key'] as String?,
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import 'package:PiliPlus/models/model_owner.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/cnt_info.dart';
|
||||
import 'package:PiliPlus/models_new/media_list/badge.dart';
|
||||
import 'package:PiliPlus/models_new/media_list/coin.dart';
|
||||
import 'package:PiliPlus/models_new/media_list/ogv_info.dart';
|
||||
import 'package:PiliPlus/models_new/media_list/page.dart';
|
||||
import 'package:PiliPlus/models_new/media_list/rights.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/episode.dart';
|
||||
|
||||
class MediaListItemModel {
|
||||
class MediaListItemModel extends BaseEpisodeItem {
|
||||
@override
|
||||
int? get id => aid;
|
||||
int? aid;
|
||||
int? offset;
|
||||
int? index;
|
||||
String? intro;
|
||||
@@ -17,32 +16,29 @@ class MediaListItemModel {
|
||||
int? tid;
|
||||
int? copyRight;
|
||||
CntInfo? cntInfo;
|
||||
String? cover;
|
||||
int? duration;
|
||||
int? pubtime;
|
||||
int? likeState;
|
||||
int? favState;
|
||||
int? page;
|
||||
List<Page>? pages;
|
||||
String? title;
|
||||
int? type;
|
||||
Owner? upper;
|
||||
String? link;
|
||||
String? bvid;
|
||||
String? shortLink;
|
||||
Rights? rights;
|
||||
dynamic elecInfo;
|
||||
Coin? coin;
|
||||
OgvInfo? ogvInfo;
|
||||
double? progressPercent;
|
||||
Badge? badge;
|
||||
bool? forbidFav;
|
||||
int? moreType;
|
||||
int? businessOid;
|
||||
int? cid;
|
||||
@override
|
||||
int? get cid => pages?.firstOrNull?.id;
|
||||
|
||||
MediaListItemModel({
|
||||
this.aid,
|
||||
super.aid,
|
||||
this.offset,
|
||||
this.index,
|
||||
this.intro,
|
||||
@@ -50,29 +46,29 @@ class MediaListItemModel {
|
||||
this.tid,
|
||||
this.copyRight,
|
||||
this.cntInfo,
|
||||
this.cover,
|
||||
super.cover,
|
||||
this.duration,
|
||||
this.pubtime,
|
||||
this.likeState,
|
||||
this.favState,
|
||||
this.page,
|
||||
this.pages,
|
||||
this.title,
|
||||
super.title,
|
||||
this.type,
|
||||
this.upper,
|
||||
this.link,
|
||||
this.bvid,
|
||||
super.bvid,
|
||||
this.shortLink,
|
||||
this.rights,
|
||||
this.elecInfo,
|
||||
this.coin,
|
||||
this.ogvInfo,
|
||||
this.progressPercent,
|
||||
this.badge,
|
||||
super.badge,
|
||||
this.forbidFav,
|
||||
this.moreType,
|
||||
this.businessOid,
|
||||
this.cid,
|
||||
super.cid,
|
||||
});
|
||||
|
||||
MediaListItemModel.fromJson(Map<String, dynamic> json) {
|
||||
@@ -106,10 +102,9 @@ class MediaListItemModel {
|
||||
? null
|
||||
: OgvInfo.fromJson(json['ogv_info']);
|
||||
progressPercent = (json['progress_percent'] as num?)?.toDouble();
|
||||
badge = json['badge'] == null ? null : Badge.fromJson(json['badge']);
|
||||
badge = json['badge']?['text'];
|
||||
forbidFav = json['forbid_fav'] as bool?;
|
||||
moreType = json['more_type'] as int?;
|
||||
businessOid = json['business_oid'] as int?;
|
||||
cid = pages.getOrNull((page ?? 1) - 1)?.id;
|
||||
}
|
||||
}
|
||||
|
||||
26
lib/models_new/pgc/pgc_info_model/brief.dart
Normal file
26
lib/models_new/pgc/pgc_info_model/brief.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
class Brief {
|
||||
List<Img>? img;
|
||||
|
||||
Brief({
|
||||
this.img,
|
||||
});
|
||||
|
||||
factory Brief.fromJson(Map<String, dynamic> json) => Brief(
|
||||
img: (json['img'] as List?)?.map((e) => Img.fromJson(e)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
class Img {
|
||||
num aspectRatio;
|
||||
String? url;
|
||||
|
||||
Img({
|
||||
required this.aspectRatio,
|
||||
this.url,
|
||||
});
|
||||
|
||||
factory Img.fromJson(Map<String, dynamic> json) => Img(
|
||||
aspectRatio: json['aspect_ratio'] ?? 1,
|
||||
url: json['url'] as String?,
|
||||
);
|
||||
}
|
||||
20
lib/models_new/pgc/pgc_info_model/cooperator.dart
Normal file
20
lib/models_new/pgc/pgc_info_model/cooperator.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
class Cooperator {
|
||||
int? mid;
|
||||
String? avatar;
|
||||
String? nickName;
|
||||
String? role;
|
||||
|
||||
Cooperator({
|
||||
this.mid,
|
||||
this.avatar,
|
||||
this.nickName,
|
||||
this.role,
|
||||
});
|
||||
|
||||
factory Cooperator.fromJson(Map<String, dynamic> json) => Cooperator(
|
||||
mid: json['mid'] as int?,
|
||||
avatar: json['avatar'] as String?,
|
||||
nickName: json['nick_name'] as String?,
|
||||
role: json['role'] as String?,
|
||||
);
|
||||
}
|
||||
@@ -8,18 +8,16 @@ import 'package:PiliPlus/models_new/video/video_detail/episode.dart'
|
||||
class EpisodeItem extends BaseEpisodeItem {
|
||||
BadgeInfo? badgeInfo;
|
||||
int? badgeType;
|
||||
String? cover;
|
||||
Dimension? dimension;
|
||||
int? duration;
|
||||
int? duration; // pgc: millisec , pugv: sec
|
||||
bool? enableVt;
|
||||
int? epId;
|
||||
String? from;
|
||||
bool? isViewHide;
|
||||
String? link;
|
||||
String? longTitle;
|
||||
int? pubTime;
|
||||
int? pv;
|
||||
String? releaseDate;
|
||||
// String? releaseDate;
|
||||
Rights? rights;
|
||||
int? sectionType;
|
||||
String? shareCopy;
|
||||
@@ -31,6 +29,7 @@ class EpisodeItem extends BaseEpisodeItem {
|
||||
int? status;
|
||||
String? subtitle;
|
||||
String? vid;
|
||||
int? play;
|
||||
|
||||
EpisodeItem({
|
||||
super.aid,
|
||||
@@ -39,11 +38,11 @@ class EpisodeItem extends BaseEpisodeItem {
|
||||
this.badgeType,
|
||||
super.bvid,
|
||||
super.cid,
|
||||
this.cover,
|
||||
super.cover,
|
||||
this.dimension,
|
||||
this.duration,
|
||||
this.enableVt,
|
||||
this.epId,
|
||||
super.epId,
|
||||
this.from,
|
||||
super.id,
|
||||
this.isViewHide,
|
||||
@@ -51,7 +50,7 @@ class EpisodeItem extends BaseEpisodeItem {
|
||||
this.longTitle,
|
||||
this.pubTime,
|
||||
this.pv,
|
||||
this.releaseDate,
|
||||
// this.releaseDate,
|
||||
this.rights,
|
||||
this.sectionType,
|
||||
this.shareCopy,
|
||||
@@ -64,6 +63,7 @@ class EpisodeItem extends BaseEpisodeItem {
|
||||
this.subtitle,
|
||||
super.title,
|
||||
this.vid,
|
||||
this.play,
|
||||
});
|
||||
|
||||
factory EpisodeItem.fromJson(Map<String, dynamic> json) => EpisodeItem(
|
||||
@@ -87,9 +87,9 @@ class EpisodeItem extends BaseEpisodeItem {
|
||||
isViewHide: json['is_view_hide'] as bool?,
|
||||
link: json['link'] as String?,
|
||||
longTitle: json['long_title'] as String?,
|
||||
pubTime: json['pub_time'] as int?,
|
||||
pubTime: json['pub_time'] ?? json['release_date'],
|
||||
pv: json['pv'] as int?,
|
||||
releaseDate: json['release_date'] as String?,
|
||||
// releaseDate: json['release_date'] as String?,
|
||||
rights: json['rights'] == null
|
||||
? null
|
||||
: Rights.fromJson(json['rights'] as Map<String, dynamic>),
|
||||
@@ -106,5 +106,6 @@ class EpisodeItem extends BaseEpisodeItem {
|
||||
subtitle: json['subtitle'] as String?,
|
||||
title: json['title'] as String?,
|
||||
vid: json['vid'] as String?,
|
||||
play: json['play'] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/activity.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/area.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/brief.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/cooperator.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/icon_font.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/new_ep.dart';
|
||||
@@ -53,6 +55,8 @@ class PgcInfoModel {
|
||||
int? type;
|
||||
UpInfo? upInfo;
|
||||
UserStatus? userStatus;
|
||||
List<Cooperator>? cooperators;
|
||||
Brief? brief;
|
||||
|
||||
PgcInfoModel({
|
||||
this.activity,
|
||||
@@ -94,6 +98,8 @@ class PgcInfoModel {
|
||||
this.type,
|
||||
this.upInfo,
|
||||
this.userStatus,
|
||||
this.cooperators,
|
||||
this.brief,
|
||||
});
|
||||
|
||||
factory PgcInfoModel.fromJson(Map<String, dynamic> json) => PgcInfoModel(
|
||||
@@ -164,5 +170,11 @@ class PgcInfoModel {
|
||||
userStatus: json['user_status'] == null
|
||||
? null
|
||||
: UserStatus.fromJson(json['user_status'] as Map<String, dynamic>),
|
||||
cooperators: (json['cooperators'] as List?)
|
||||
?.map((e) => Cooperator.fromJson(e))
|
||||
.toList(),
|
||||
brief: json['brief'] == null
|
||||
? null
|
||||
: Brief.fromJson(json['brief'] as Map<String, dynamic>),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class UserStatus {
|
||||
int? payPackPaid;
|
||||
int? sponsor;
|
||||
UserProgress? progress;
|
||||
int? favored;
|
||||
|
||||
UserStatus({
|
||||
this.areaLimit,
|
||||
@@ -21,6 +22,7 @@ class UserStatus {
|
||||
this.payPackPaid,
|
||||
this.sponsor,
|
||||
this.progress,
|
||||
this.favored,
|
||||
});
|
||||
|
||||
factory UserStatus.fromJson(Map<String, dynamic> json) => UserStatus(
|
||||
@@ -35,5 +37,6 @@ class UserStatus {
|
||||
progress: json['progress'] == null
|
||||
? null
|
||||
: UserProgress.fromJson(json['progress']),
|
||||
favored: json['favored'] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
19
lib/models_new/space/space_cheese/data.dart
Normal file
19
lib/models_new/space/space_cheese/data.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/item.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/page.dart';
|
||||
|
||||
class SpaceCheeseData {
|
||||
List<SpaceCheeseItem>? items;
|
||||
SpaceCheesePage? page;
|
||||
|
||||
SpaceCheeseData({this.items, this.page});
|
||||
|
||||
factory SpaceCheeseData.fromJson(Map<String, dynamic> json) =>
|
||||
SpaceCheeseData(
|
||||
items: (json['items'] as List<dynamic>?)
|
||||
?.map((e) => SpaceCheeseItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
page: json['page'] == null
|
||||
? null
|
||||
: SpaceCheesePage.fromJson(json['page'] as Map<String, dynamic>),
|
||||
);
|
||||
}
|
||||
48
lib/models_new/space/space_cheese/item.dart
Normal file
48
lib/models_new/space/space_cheese/item.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
class SpaceCheeseItem {
|
||||
bool? cooperated;
|
||||
String? cooperationMark;
|
||||
String? cover;
|
||||
int? epCount;
|
||||
String? link;
|
||||
List<String>? marks;
|
||||
int? page;
|
||||
int? play;
|
||||
int? seasonId;
|
||||
String? status;
|
||||
String? subtitle;
|
||||
String? title;
|
||||
String? ctime;
|
||||
|
||||
SpaceCheeseItem({
|
||||
this.cooperated,
|
||||
this.cooperationMark,
|
||||
this.cover,
|
||||
this.epCount,
|
||||
this.link,
|
||||
this.marks,
|
||||
this.page,
|
||||
this.play,
|
||||
this.seasonId,
|
||||
this.status,
|
||||
this.subtitle,
|
||||
this.title,
|
||||
this.ctime,
|
||||
});
|
||||
|
||||
factory SpaceCheeseItem.fromJson(Map<String, dynamic> json) =>
|
||||
SpaceCheeseItem(
|
||||
cooperated: json['cooperated'] as bool?,
|
||||
cooperationMark: json['cooperation_mark'] as String?,
|
||||
cover: json['cover'] as String?,
|
||||
epCount: json['ep_count'] as int?,
|
||||
link: json['link'] as String?,
|
||||
marks: (json['marks'] as List?)?.cast(),
|
||||
page: json['page'] as int?,
|
||||
play: json['play'] as int?,
|
||||
seasonId: json['season_id'] as int?,
|
||||
status: json['status'] as String?,
|
||||
subtitle: json['subtitle'] as String?,
|
||||
title: json['title'] as String?,
|
||||
ctime: json['ctime'] as String?,
|
||||
);
|
||||
}
|
||||
16
lib/models_new/space/space_cheese/page.dart
Normal file
16
lib/models_new/space/space_cheese/page.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
class SpaceCheesePage {
|
||||
bool? next;
|
||||
int? num;
|
||||
int? size;
|
||||
int? total;
|
||||
|
||||
SpaceCheesePage({this.next, this.num, this.size, this.total});
|
||||
|
||||
factory SpaceCheesePage.fromJson(Map<String, dynamic> json) =>
|
||||
SpaceCheesePage(
|
||||
next: json['next'] as bool?,
|
||||
num: json['num'] as int?,
|
||||
size: json['size'] as int?,
|
||||
total: json['total'] as int?,
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
import 'package:PiliPlus/models_new/video/video_detail/arc.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/page.dart';
|
||||
|
||||
abstract class BaseEpisodeItem {
|
||||
class BaseEpisodeItem {
|
||||
int? id;
|
||||
int? aid;
|
||||
int? cid;
|
||||
int? epId;
|
||||
String? bvid;
|
||||
String? badge;
|
||||
String? title;
|
||||
String? cover;
|
||||
|
||||
BaseEpisodeItem({
|
||||
this.id,
|
||||
this.aid,
|
||||
this.cid,
|
||||
this.epId,
|
||||
this.bvid,
|
||||
this.badge,
|
||||
this.title,
|
||||
this.cover,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,6 +30,8 @@ class EpisodeItem extends BaseEpisodeItem {
|
||||
Arc? arc;
|
||||
Part? page;
|
||||
List<Part>? pages;
|
||||
@override
|
||||
String? get cover => arc?.pic;
|
||||
|
||||
EpisodeItem({
|
||||
this.seasonId,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:PiliPlus/models_new/video/video_detail/dimension.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/episode.dart';
|
||||
|
||||
class Part {
|
||||
int? cid;
|
||||
class Part extends BaseEpisodeItem {
|
||||
int? page;
|
||||
String? from;
|
||||
String? pagePart;
|
||||
@@ -11,10 +11,9 @@ class Part {
|
||||
Dimension? dimension;
|
||||
int? ctime;
|
||||
String? firstFrame;
|
||||
String? badge;
|
||||
|
||||
Part({
|
||||
this.cid,
|
||||
super.cid,
|
||||
this.page,
|
||||
this.from,
|
||||
this.pagePart,
|
||||
@@ -24,7 +23,7 @@ class Part {
|
||||
this.dimension,
|
||||
this.ctime,
|
||||
this.firstFrame,
|
||||
this.badge,
|
||||
super.badge,
|
||||
});
|
||||
|
||||
factory Part.fromJson(Map<String, dynamic> json) => Part(
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
class Choice {
|
||||
int? id;
|
||||
import 'package:PiliPlus/models_new/video/video_detail/episode.dart';
|
||||
|
||||
class Choice extends BaseEpisodeItem {
|
||||
String? platformAction;
|
||||
String? nativeAction;
|
||||
String? condition;
|
||||
int? cid;
|
||||
String? option;
|
||||
int? isDefault;
|
||||
|
||||
Choice({
|
||||
this.id,
|
||||
super.id,
|
||||
this.platformAction,
|
||||
this.nativeAction,
|
||||
this.condition,
|
||||
this.cid,
|
||||
super.cid,
|
||||
this.option,
|
||||
this.isDefault,
|
||||
});
|
||||
|
||||
@@ -276,7 +276,7 @@ Commit Hash: ${BuildConfig.commitHash}''',
|
||||
);
|
||||
Accounts.account
|
||||
.putAll(res)
|
||||
.whenComplete(() => Accounts.refresh())
|
||||
.whenComplete(Accounts.refresh)
|
||||
.whenComplete(() {
|
||||
MineController.anonymity.value =
|
||||
!Accounts.get(
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:PiliPlus/http/dynamics.dart';
|
||||
import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/common/account_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/article_content_model.dart'
|
||||
show ArticleContentModel;
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
@@ -14,6 +13,7 @@ import 'package:PiliPlus/models_new/article/article_info/data.dart';
|
||||
import 'package:PiliPlus/models_new/article/article_view/data.dart';
|
||||
import 'package:PiliPlus/pages/common/dyn/common_dyn_controller.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/url_utils.dart';
|
||||
import 'package:flutter/rendering.dart' show ScrollDirection;
|
||||
@@ -165,7 +165,7 @@ class ArticleController extends CommonDynController<MainListReply> {
|
||||
}
|
||||
if (isLoaded.value) {
|
||||
queryData();
|
||||
if (Accounts.get(AccountType.heartbeat).isLogin && !Pref.historyPause) {
|
||||
if (Accounts.heartbeat.isLogin && !Pref.historyPause) {
|
||||
VideoHttp.historyReport(aid: commentId, type: 5);
|
||||
}
|
||||
}
|
||||
@@ -177,15 +177,13 @@ class ArticleController extends CommonDynController<MainListReply> {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<MainListReply>> customGetData() {
|
||||
return ReplyGrpc.mainList(
|
||||
type: commentType,
|
||||
oid: commentId,
|
||||
mode: mode.value,
|
||||
cursorNext: cursorNext,
|
||||
offset: paginationReply?.nextOffset,
|
||||
);
|
||||
}
|
||||
Future<LoadingState<MainListReply>> customGetData() => ReplyGrpc.mainList(
|
||||
type: commentType,
|
||||
oid: commentId,
|
||||
mode: mode.value,
|
||||
cursorNext: cursorNext,
|
||||
offset: paginationReply?.nextOffset,
|
||||
);
|
||||
|
||||
Future<void> onFav() async {
|
||||
final favorite = stats.value?.favorite;
|
||||
@@ -197,11 +195,10 @@ class ArticleController extends CommonDynController<MainListReply> {
|
||||
: await FavHttp.communityAction(opusId: id, action: isFav ? 4 : 3);
|
||||
if (res['status']) {
|
||||
favorite?.status = !isFav;
|
||||
var count = favorite?.count ?? 0;
|
||||
if (isFav) {
|
||||
favorite?.count = count - 1;
|
||||
favorite?.count--;
|
||||
} else {
|
||||
favorite?.count = count + 1;
|
||||
favorite?.count++;
|
||||
}
|
||||
stats.refresh();
|
||||
SmartDialog.showToast('${isFav ? '取消' : ''}收藏成功');
|
||||
@@ -219,11 +216,10 @@ class ArticleController extends CommonDynController<MainListReply> {
|
||||
);
|
||||
if (res['status']) {
|
||||
like?.status = !isLike;
|
||||
int count = like?.count ?? 0;
|
||||
if (isLike) {
|
||||
like?.count = count - 1;
|
||||
like?.count--;
|
||||
} else {
|
||||
like?.count = count + 1;
|
||||
like?.count++;
|
||||
}
|
||||
stats.refresh();
|
||||
SmartDialog.showToast(!isLike ? '点赞成功' : '取消赞');
|
||||
|
||||
@@ -20,8 +20,8 @@ import 'package:PiliPlus/pages/article/widgets/opus_content.dart';
|
||||
import 'package:PiliPlus/pages/common/dyn/common_dyn_page.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_repost/view.dart';
|
||||
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/image_util.dart';
|
||||
@@ -35,7 +35,7 @@ import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
import 'package:html/parser.dart' as parser;
|
||||
|
||||
class ArticlePage extends CommonDynPage {
|
||||
@@ -302,7 +302,7 @@ class _ArticlePageState extends CommonDynPageState<ArticlePage> {
|
||||
final pic = pics[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => context.imageView(
|
||||
onTap: () => PageUtils.imageView(
|
||||
quality: 60,
|
||||
imgList: pics
|
||||
.map((e) => SourceModel(url: e.url!))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:PiliPlus/models/common/image_preview_type.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/image_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -54,7 +54,7 @@ Widget htmlRender({
|
||||
if (callback != null) {
|
||||
callback([imgUrl], 0);
|
||||
} else {
|
||||
context.imageView(
|
||||
PageUtils.imageView(
|
||||
imgList: [SourceModel(url: imgUrl)],
|
||||
quality: 60,
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/image_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:cached_network_svg_image/cached_network_svg_image.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
@@ -181,7 +182,7 @@ class OpusContent extends StatelessWidget {
|
||||
if (callback != null) {
|
||||
callback!([pic.url!], 0);
|
||||
} else {
|
||||
context.imageView(
|
||||
PageUtils.imageView(
|
||||
imgList: [SourceModel(url: pic.url!)],
|
||||
quality: 60,
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/common/video/source_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_folder/data.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/data.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/stat_detail.dart';
|
||||
@@ -21,9 +22,8 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
abstract class CommonIntroController extends GetxController {
|
||||
String heroTag = Get.arguments['heroTag'];
|
||||
|
||||
String bvid = Get.parameters['bvid']!;
|
||||
late final String heroTag;
|
||||
late String bvid;
|
||||
|
||||
// 是否点赞
|
||||
final RxBool hasLike = false.obs;
|
||||
@@ -49,7 +49,7 @@ abstract class CommonIntroController extends GetxController {
|
||||
|
||||
final Rx<VideoDetailData> videoDetail = VideoDetailData().obs;
|
||||
|
||||
Future<void> queryVideoIntro();
|
||||
void queryVideoIntro();
|
||||
|
||||
bool prevPlay();
|
||||
bool nextPlay();
|
||||
@@ -59,11 +59,17 @@ abstract class CommonIntroController extends GetxController {
|
||||
late final RxString total = '1'.obs;
|
||||
Timer? timer;
|
||||
|
||||
final RxInt cid = int.parse(Get.parameters['cid']!).obs;
|
||||
late final RxInt cid;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
heroTag = args['heroTag'];
|
||||
bvid = args['bvid'];
|
||||
cid = RxInt(args['cid']);
|
||||
hasLater.value = args['sourceType'] == SourceType.watchLater;
|
||||
|
||||
queryVideoIntro();
|
||||
startTimer();
|
||||
}
|
||||
@@ -264,7 +270,7 @@ abstract class CommonIntroController extends GetxController {
|
||||
|
||||
Future<void> viewLater() async {
|
||||
var res = await (hasLater.value
|
||||
? UserHttp.toViewDel(aids: [IdUtils.bv2av(bvid)])
|
||||
? UserHttp.toViewDel(aids: IdUtils.bv2av(bvid).toString())
|
||||
: await UserHttp.toViewLater(bvid: bvid));
|
||||
if (res['status']) hasLater.value = !hasLater.value;
|
||||
SmartDialog.showToast(res['msg']);
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_search_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
abstract class CommonSearchPage extends StatefulWidget {
|
||||
const CommonSearchPage({super.key});
|
||||
}
|
||||
|
||||
abstract class CommonSearchPageState<S extends CommonSearchPage, R, T>
|
||||
extends State<S> {
|
||||
CommonSearchController<R, T> get controller;
|
||||
|
||||
List<Widget>? extraActions;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '搜索',
|
||||
onPressed: controller.onRefresh,
|
||||
icon: const Icon(Icons.search_outlined, size: 22),
|
||||
),
|
||||
...?extraActions,
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
title: TextField(
|
||||
autofocus: true,
|
||||
focusNode: controller.focusNode,
|
||||
controller: controller.editController,
|
||||
textInputAction: TextInputAction.search,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
decoration: InputDecoration(
|
||||
hintText: '搜索',
|
||||
border: InputBorder.none,
|
||||
suffixIcon: IconButton(
|
||||
tooltip: '清空',
|
||||
icon: const Icon(Icons.clear, size: 22),
|
||||
onPressed: () => controller
|
||||
..loadingState.value = LoadingState.loading()
|
||||
..onClear()
|
||||
..focusNode.requestFocus(),
|
||||
),
|
||||
),
|
||||
onSubmitted: (value) => controller.onRefresh(),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: controller.scrollController,
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: Obx(() => _buildBody(controller.loadingState.value)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState<List<T>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => const HttpError(),
|
||||
Success(:var response) =>
|
||||
response?.isNotEmpty == true
|
||||
? buildList(response!)
|
||||
: HttpError(
|
||||
onReload: controller.onReload,
|
||||
),
|
||||
Error(:var errMsg) => HttpError(
|
||||
errMsg: errMsg,
|
||||
onReload: controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Widget buildList(List<T> list);
|
||||
}
|
||||
@@ -7,11 +7,12 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/dyn/common_dyn_controller.dart';
|
||||
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_reply/view.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/num_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
|
||||
abstract class CommonDynPage extends StatefulWidget {
|
||||
const CommonDynPage({super.key});
|
||||
|
||||
144
lib/pages/common/multi_select/base.dart
Normal file
144
lib/pages/common/multi_select/base.dart
Normal file
@@ -0,0 +1,144 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
mixin MultiSelectData {
|
||||
bool? checked;
|
||||
}
|
||||
|
||||
abstract class MultiSelectBase<T> {
|
||||
RxBool get enableMultiSelect;
|
||||
RxBool get allSelected;
|
||||
|
||||
int get checkedCount;
|
||||
|
||||
void onSelect(T item, [bool disableSelect = true]);
|
||||
void handleSelect([bool checked = false, bool disableSelect = true]);
|
||||
void onRemove();
|
||||
}
|
||||
|
||||
mixin CommonMultiSelectMixin<T extends MultiSelectData>
|
||||
implements MultiSelectBase<T> {
|
||||
@override
|
||||
late final RxBool enableMultiSelect = false.obs;
|
||||
@override
|
||||
late final allSelected = false.obs;
|
||||
|
||||
Rx<LoadingState<List<T>?>> get loadingState;
|
||||
late final RxInt rxCount = 0.obs;
|
||||
|
||||
@override
|
||||
int get checkedCount => rxCount.value;
|
||||
|
||||
Iterable<T> get allChecked =>
|
||||
loadingState.value.data!.where((v) => v.checked == true);
|
||||
|
||||
@override
|
||||
void onSelect(T item, [bool disableSelect = true]) {
|
||||
List<T> list = loadingState.value.data!;
|
||||
item.checked = !(item.checked ?? false);
|
||||
if (item.checked!) {
|
||||
rxCount.value++;
|
||||
} else {
|
||||
rxCount.value--;
|
||||
}
|
||||
loadingState.refresh();
|
||||
if (disableSelect) {
|
||||
if (checkedCount == 0) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
} else {
|
||||
allSelected.value = checkedCount == list.length;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleSelect([bool checked = false, bool disableSelect = true]) {
|
||||
if (loadingState.value.isSuccess) {
|
||||
final list = loadingState.value.data;
|
||||
if (list?.isNotEmpty == true) {
|
||||
for (var item in list!) {
|
||||
item.checked = checked;
|
||||
}
|
||||
loadingState.refresh();
|
||||
rxCount.value = checked ? list.length : 0;
|
||||
}
|
||||
}
|
||||
if (disableSelect && !checked) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixin DeleteItemMixin<R, T extends MultiSelectData>
|
||||
on CommonListController<R, T>, CommonMultiSelectMixin<T> {
|
||||
Future<void> afterDelete(Set<T> removeList) async {
|
||||
final list = loadingState.value.data!;
|
||||
if (removeList.length == list.length) {
|
||||
list.clear();
|
||||
} else if (removeList.length == 1) {
|
||||
list.remove(removeList.first);
|
||||
} else {
|
||||
list.removeWhere(removeList.contains);
|
||||
}
|
||||
if (list.isNotEmpty || isEnd) {
|
||||
loadingState.refresh();
|
||||
} else {
|
||||
onReload();
|
||||
}
|
||||
if (enableMultiSelect.value) {
|
||||
rxCount.value = 0;
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// abstract class SetMultiSelectController<R, T, I>
|
||||
// extends CommonListController<R, T>
|
||||
// with MultiSelectMixin<T>, SetCommonMultiSelectMixin<T, I> {}
|
||||
|
||||
// mixin SetCommonMultiSelectMixin<T, R> on MultiSelectMixin<T> {
|
||||
// Rx<LoadingState<List<T>?>> get loadingState;
|
||||
// RxSet<R> get selected;
|
||||
|
||||
// @override
|
||||
// int get checkedCount => selected.length;
|
||||
|
||||
// R getId(T item);
|
||||
|
||||
// @override
|
||||
// void onSelect(T item, [bool disableSelect = true]) {
|
||||
// final id = getId(item);
|
||||
// if (selected.contains(id)) {
|
||||
// selected.remove(id);
|
||||
// } else {
|
||||
// selected.add(id);
|
||||
// }
|
||||
// loadingState.refresh();
|
||||
// if (disableSelect) {
|
||||
// if (checkedCount == 0) {
|
||||
// enableMultiSelect.value = false;
|
||||
// }
|
||||
// } else {
|
||||
// allSelected.value = checkedCount == loadingState.value.data!.length;
|
||||
// }
|
||||
// }
|
||||
|
||||
// @override
|
||||
// void handleSelect([bool checked = false, bool disableSelect = true]) {
|
||||
// if (loadingState.value.isSuccess) {
|
||||
// final list = loadingState.value.data;
|
||||
// if (list?.isNotEmpty == true) {
|
||||
// if (checked) {
|
||||
// selected.addAll(list!.map(getId));
|
||||
// } else {
|
||||
// selected.clear();
|
||||
// }
|
||||
// loadingState.refresh();
|
||||
// }
|
||||
// }
|
||||
// if (disableSelect && !checked) {
|
||||
// enableMultiSelect.value = false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,6 @@
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
|
||||
abstract class MultiSelectController<R, T extends MultiSelectData>
|
||||
extends CommonListController<R, T>
|
||||
with CommonMultiSelectMixin<T>, DeleteItemMixin {}
|
||||
@@ -1,43 +0,0 @@
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
mixin 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;
|
||||
|
||||
void onSelect(T item, [bool disableSelect = true]) {
|
||||
List<T> list = loadingState.value.data!;
|
||||
item.checked = !(item.checked ?? false);
|
||||
checkedCount.value = list.where((item) => item.checked == true).length;
|
||||
loadingState.refresh();
|
||||
if (disableSelect) {
|
||||
if (checkedCount.value == 0) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
} else {
|
||||
allSelected.value = checkedCount.value == list.length;
|
||||
}
|
||||
}
|
||||
|
||||
void handleSelect([bool checked = false, bool disableSelect = true]) {
|
||||
if (loadingState.value.isSuccess) {
|
||||
List<T>? list = loadingState.value.data;
|
||||
if (list?.isNotEmpty == true) {
|
||||
for (T item in list!) {
|
||||
item.checked = checked;
|
||||
}
|
||||
loadingState.refresh();
|
||||
checkedCount.value = checked ? list.length : 0;
|
||||
}
|
||||
}
|
||||
if (disableSelect && !checked) {
|
||||
enableMultiSelect.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@ import 'dart:math' show max;
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/models/common/publish_panel_type.dart';
|
||||
import 'package:PiliPlus/models_new/upload_bfs/data.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:chat_bottom_container/chat_bottom_container.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
abstract class CommonPublishPage<T> extends StatefulWidget {
|
||||
@@ -255,5 +256,5 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
|
||||
enablePublish.value = value.trim().isNotEmpty;
|
||||
}
|
||||
|
||||
void onSave() {}
|
||||
void onSave();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:PiliPlus/models_new/emote/emote.dart' as e;
|
||||
import 'package:PiliPlus/models_new/live/live_emote/emoticon.dart';
|
||||
import 'package:PiliPlus/pages/common/publish/common_publish_page.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_mention/view.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -75,7 +75,7 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
controller.keepChatPanel();
|
||||
await context.imageView(
|
||||
await PageUtils.imageView(
|
||||
imgList: pathList
|
||||
.map(
|
||||
(path) => SourceModel(
|
||||
@@ -228,22 +228,23 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
"biz_id": "",
|
||||
});
|
||||
case RichTextType.vote:
|
||||
list.add({
|
||||
"raw_text": e.rawText,
|
||||
"type": 4,
|
||||
"biz_id": e.id,
|
||||
});
|
||||
list.add({
|
||||
"raw_text": ' ',
|
||||
"type": 1,
|
||||
"biz_id": "",
|
||||
});
|
||||
list
|
||||
..add({
|
||||
"raw_text": e.rawText,
|
||||
"type": 4,
|
||||
"biz_id": e.id,
|
||||
})
|
||||
..add({
|
||||
"raw_text": ' ',
|
||||
"type": 1,
|
||||
"biz_id": "",
|
||||
});
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
double _mentionOffset = 0;
|
||||
late double _mentionOffset = 0;
|
||||
Future<void> onMention([bool fromClick = false]) async {
|
||||
controller.keepChatPanel();
|
||||
final res = await DynMentionPanel.onDynMention(
|
||||
|
||||
122
lib/pages/common/search/common_search_page.dart
Normal file
122
lib/pages/common/search/common_search_page.dart
Normal file
@@ -0,0 +1,122 @@
|
||||
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/search/common_search_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
abstract class CommonSearchPage extends StatefulWidget {
|
||||
const CommonSearchPage({super.key});
|
||||
}
|
||||
|
||||
abstract class CommonSearchPageState<S extends CommonSearchPage, R, T>
|
||||
extends State<S> {
|
||||
CommonSearchController<R, T> get controller;
|
||||
|
||||
List<Widget>? get extraActions => null;
|
||||
|
||||
List<Widget>? get multiSelectChildren => null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (controller case MultiSelectBase multiCtr) {
|
||||
return Obx(() {
|
||||
final enableMultiSelect = multiCtr.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
multiCtr.handleSelect();
|
||||
}
|
||||
},
|
||||
child: _build(true),
|
||||
);
|
||||
});
|
||||
}
|
||||
return _build(false);
|
||||
}
|
||||
|
||||
Widget _build(bool multiSelect) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: _buildBar(multiSelect),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: controller.scrollController,
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: Obx(() => _buildBody(controller.loadingState.value)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildBar(bool multiSelect) {
|
||||
final AppBar bar = AppBar(
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '搜索',
|
||||
onPressed: controller.onRefresh,
|
||||
icon: const Icon(Icons.search_outlined, size: 22),
|
||||
),
|
||||
...?extraActions,
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
title: TextField(
|
||||
autofocus: true,
|
||||
focusNode: controller.focusNode,
|
||||
controller: controller.editController,
|
||||
textInputAction: TextInputAction.search,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
decoration: InputDecoration(
|
||||
hintText: '搜索',
|
||||
border: InputBorder.none,
|
||||
suffixIcon: IconButton(
|
||||
tooltip: '清空',
|
||||
icon: const Icon(Icons.clear, size: 22),
|
||||
onPressed: () => controller
|
||||
..loadingState.value = LoadingState.loading()
|
||||
..onClear()
|
||||
..focusNode.requestFocus(),
|
||||
),
|
||||
),
|
||||
onSubmitted: (value) => controller.onRefresh(),
|
||||
),
|
||||
);
|
||||
if (multiSelect) {
|
||||
return MultiSelectAppBarWidget(
|
||||
ctr: controller as MultiSelectBase,
|
||||
children: multiSelectChildren,
|
||||
child: bar,
|
||||
);
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState<List<T>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => const HttpError(),
|
||||
Success(:var response) =>
|
||||
response?.isNotEmpty == true
|
||||
? buildList(response!)
|
||||
: HttpError(
|
||||
onReload: controller.onReload,
|
||||
),
|
||||
Error(:var errMsg) => HttpError(
|
||||
errMsg: errMsg,
|
||||
onReload: controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Widget buildList(List<T> list);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:PiliPlus/pages/common/common_slide_page.dart';
|
||||
import 'package:PiliPlus/pages/common/slide/common_slide_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class CommonCollapseSlidePage extends CommonSlidePage {
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:PiliPlus/grpc/bilibili/community/service/dm/v1.pb.dart';
|
||||
@@ -14,13 +13,15 @@ import 'package:get/get.dart';
|
||||
class PlDanmaku extends StatefulWidget {
|
||||
final int cid;
|
||||
final PlPlayerController playerController;
|
||||
final bool? isPipMode;
|
||||
final bool isPipMode;
|
||||
final bool isFullScreen;
|
||||
|
||||
const PlDanmaku({
|
||||
super.key,
|
||||
required this.cid,
|
||||
required this.playerController,
|
||||
this.isPipMode,
|
||||
this.isPipMode = false,
|
||||
required this.isFullScreen,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -33,8 +34,6 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
late PlDanmakuController _plDanmakuController;
|
||||
DanmakuController? _controller;
|
||||
int latestAddedPosition = -1;
|
||||
bool? _isFullScreen;
|
||||
StreamSubscription? _listenerFS;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -53,20 +52,27 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
playerController
|
||||
..addStatusLister(playerListener)
|
||||
..addPositionListener(videoPositionListen);
|
||||
_listenerFS = playerController.isFullScreen.listen((isFullScreen) {
|
||||
if (isFullScreen != _isFullScreen) {
|
||||
_isFullScreen = isFullScreen;
|
||||
if (_controller != null) {
|
||||
_controller!.updateOption(
|
||||
_controller!.option.copyWith(
|
||||
fontSize: _getFontSize(isFullScreen),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(PlDanmaku oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.isPipMode != widget.isPipMode ||
|
||||
oldWidget.isFullScreen != widget.isFullScreen) {
|
||||
_updateFontSize();
|
||||
}
|
||||
}
|
||||
|
||||
void _updateFontSize() {
|
||||
_controller?.updateOption(
|
||||
_controller!.option.copyWith(fontSize: _fontSize),
|
||||
);
|
||||
}
|
||||
|
||||
double get _fontSize => !widget.isFullScreen || widget.isPipMode
|
||||
? 15 * playerController.danmakuFontScale
|
||||
: 15 * playerController.danmakuFontScaleFS;
|
||||
|
||||
// 播放器状态监听
|
||||
void playerListener(PlayerStatus? status) {
|
||||
if (status == PlayerStatus.playing) {
|
||||
@@ -77,15 +83,11 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
}
|
||||
|
||||
void videoPositionListen(Duration position) {
|
||||
if (!playerController.enableShowDanmaku.value) {
|
||||
if (_controller == null || !playerController.enableShowDanmaku.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_controller == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!playerController.showDanmaku && widget.isPipMode != true) {
|
||||
if (!playerController.showDanmaku && !widget.isPipMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,7 +140,6 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_listenerFS?.cancel();
|
||||
playerController
|
||||
..removePositionListener(videoPositionListen)
|
||||
..removeStatusLister(playerListener);
|
||||
@@ -146,45 +147,35 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
double _getFontSize(bool isFullScreen) =>
|
||||
!isFullScreen || widget.isPipMode == true
|
||||
? 15 * playerController.danmakuFontScale
|
||||
: 15 * playerController.danmakuFontScaleFS;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, box) {
|
||||
// double initDuration = box.maxWidth / 12;
|
||||
return Obx(
|
||||
() => AnimatedOpacity(
|
||||
opacity: playerController.enableShowDanmaku.value ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuScreen(
|
||||
createdController: (DanmakuController e) {
|
||||
playerController.danmakuController = _controller = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: _getFontSize(playerController.isFullScreen.value),
|
||||
fontWeight: playerController.fontWeight,
|
||||
area: playerController.showArea,
|
||||
opacity: playerController.danmakuOpacity,
|
||||
hideTop: playerController.blockTypes.contains(5),
|
||||
hideScroll: playerController.blockTypes.contains(2),
|
||||
hideBottom: playerController.blockTypes.contains(4),
|
||||
duration:
|
||||
playerController.danmakuDuration /
|
||||
playerController.playbackSpeed,
|
||||
staticDuration:
|
||||
playerController.danmakuStaticDuration /
|
||||
playerController.playbackSpeed,
|
||||
strokeWidth: playerController.strokeWidth,
|
||||
lineHeight: playerController.danmakuLineHeight,
|
||||
),
|
||||
),
|
||||
return Obx(
|
||||
() => AnimatedOpacity(
|
||||
opacity: playerController.enableShowDanmaku.value ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuScreen(
|
||||
createdController: (DanmakuController e) {
|
||||
playerController.danmakuController = _controller = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: _fontSize,
|
||||
fontWeight: playerController.fontWeight,
|
||||
area: playerController.showArea,
|
||||
opacity: playerController.danmakuOpacity,
|
||||
hideTop: playerController.blockTypes.contains(5),
|
||||
hideScroll: playerController.blockTypes.contains(2),
|
||||
hideBottom: playerController.blockTypes.contains(4),
|
||||
duration:
|
||||
playerController.danmakuDuration /
|
||||
playerController.playbackSpeed,
|
||||
staticDuration:
|
||||
playerController.danmakuStaticDuration /
|
||||
playerController.playbackSpeed,
|
||||
strokeWidth: playerController.strokeWidth,
|
||||
lineHeight: playerController.danmakuLineHeight,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/controller.dart';
|
||||
import 'package:PiliPlus/pages/save_panel/view.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
@@ -19,7 +20,7 @@ import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
|
||||
class AuthorPanel extends StatelessWidget {
|
||||
final DynamicItemModel item;
|
||||
@@ -98,7 +99,7 @@ class AuthorPanel extends StatelessWidget {
|
||||
item.modules.moduleAuthor!.vip != null &&
|
||||
item.modules.moduleAuthor!.vip!.status > 0 &&
|
||||
item.modules.moduleAuthor!.vip!.type == 2
|
||||
? context.vipColor
|
||||
? theme.colorScheme.vipColor
|
||||
: theme.colorScheme.onSurface,
|
||||
fontSize: theme.textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
|
||||
@@ -6,9 +6,9 @@ import 'package:PiliPlus/pages/dynamics/widgets/author_panel.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/blocked_item.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/content_panel.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/module_panel.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class DynamicPanel extends StatelessWidget {
|
||||
final DynamicItemModel item;
|
||||
@@ -80,8 +80,7 @@ class DynamicPanel extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
if (isSave ||
|
||||
(isDetail && Get.context!.orientation == Orientation.landscape)) {
|
||||
if (isSave || (isDetail && context.isLandscape)) {
|
||||
return child;
|
||||
}
|
||||
return DecoratedBox(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
Widget livePanel(
|
||||
ThemeData theme,
|
||||
@@ -20,7 +20,7 @@ Widget livePanel(
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => Get.toNamed('/liveRoom?roomid=${content.live?.id}'),
|
||||
onTap: () => PageUtils.toLiveRoom(content.live?.id),
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
imageSaveDialog(
|
||||
|
||||
@@ -3,8 +3,8 @@ import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
Widget livePanelSub(
|
||||
ThemeData theme,
|
||||
@@ -24,7 +24,7 @@ Widget livePanelSub(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
||||
child: GestureDetector(
|
||||
onTap: () => Get.toNamed('/liveRoom?roomid=${content.roomId}'),
|
||||
onTap: () => PageUtils.toLiveRoom(content.roomId),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, box) {
|
||||
double width = box.maxWidth;
|
||||
|
||||
@@ -10,9 +10,7 @@ import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -231,10 +229,8 @@ TextSpan? richNode(
|
||||
int? cid = await SearchHttp.ab2c(bvid: i.rid);
|
||||
if (cid != null) {
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=${i.rid}&cid=$cid',
|
||||
arguments: {
|
||||
'heroTag': Utils.makeHeroTag(i.rid),
|
||||
},
|
||||
bvid: i.rid,
|
||||
cid: cid,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -276,7 +272,7 @@ TextSpan? richNode(
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
void onView(List<OpusPicModel> list) {
|
||||
Get.context!.imageView(
|
||||
PageUtils.imageView(
|
||||
imgList: list
|
||||
.map((e) => SourceModel(url: e.src!))
|
||||
.toList(),
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:PiliPlus/models/dynamics/up.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/controller.dart';
|
||||
import 'package:PiliPlus/pages/live_follow/view.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -144,7 +145,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
feedBack();
|
||||
switch (data) {
|
||||
case LiveUserItem():
|
||||
Get.toNamed('/liveRoom?roomid=${data.roomId}');
|
||||
PageUtils.toLiveRoom(data.roomId);
|
||||
case UpItem():
|
||||
_onSelect(data);
|
||||
break;
|
||||
|
||||
@@ -4,10 +4,11 @@ import 'package:PiliPlus/common/widgets/dialog/report.dart';
|
||||
import 'package:PiliPlus/http/dynamics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/dynamics/vote_model.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/num_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
|
||||
class VotePanel extends StatefulWidget {
|
||||
final VoteInfo voteInfo;
|
||||
|
||||
@@ -24,6 +24,7 @@ import 'package:PiliPlus/pages/dynamics_select_topic/view.dart';
|
||||
import 'package:PiliPlus/pages/emote/controller.dart';
|
||||
import 'package:PiliPlus/pages/emote/view.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
@@ -31,7 +32,7 @@ import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
|
||||
import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
|
||||
class CreateDynPanel extends CommonRichTextPubPage {
|
||||
const CreateDynPanel({
|
||||
|
||||
@@ -97,7 +97,7 @@ class _CreateVotePageState extends State<CreateVotePage> {
|
||||
..add(
|
||||
_buildInput(
|
||||
theme,
|
||||
key: ValueKey(e.hashCode),
|
||||
key: ObjectKey(e),
|
||||
showDel: showDel,
|
||||
onDel: () {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:PiliPlus/pages/dynamics/widgets/author_panel.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_detail/controller.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_repost/view.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/num_util.dart';
|
||||
@@ -18,7 +19,7 @@ import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
|
||||
class DynamicDetailPage extends CommonDynPage {
|
||||
const DynamicDetailPage({super.key});
|
||||
|
||||
@@ -10,10 +10,11 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models_new/dynamic/dyn_mention/group.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_mention/controller.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_mention/widgets/item.dart';
|
||||
import 'package:PiliPlus/pages/search/controller.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
|
||||
class DynMentionPanel extends StatefulWidget {
|
||||
const DynMentionPanel({
|
||||
@@ -57,10 +58,11 @@ class DynMentionPanel extends StatefulWidget {
|
||||
State<DynMentionPanel> createState() => _DynMentionPanelState();
|
||||
}
|
||||
|
||||
class _DynMentionPanelState extends State<DynMentionPanel> {
|
||||
class _DynMentionPanelState extends State<DynMentionPanel>
|
||||
with SearchKeywordMixin {
|
||||
final _controller = Get.put(DynMentionController());
|
||||
final StreamController<String> _ctr = StreamController<String>();
|
||||
late StreamSubscription<String> _sub;
|
||||
@override
|
||||
Duration get duration => const Duration(milliseconds: 300);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -68,26 +70,25 @@ class _DynMentionPanelState extends State<DynMentionPanel> {
|
||||
if (_controller.loadingState.value is Error) {
|
||||
_controller.onReload();
|
||||
}
|
||||
_sub = _ctr.stream
|
||||
.debounce(const Duration(milliseconds: 300), trailing: true)
|
||||
.listen((value) {
|
||||
_controller
|
||||
..enableClear.value = value.isNotEmpty
|
||||
..onRefresh().whenComplete(
|
||||
() => WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => widget.scrollController?.jumpToTop(),
|
||||
),
|
||||
);
|
||||
});
|
||||
subInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_sub.cancel();
|
||||
_ctr.close();
|
||||
subDispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
ValueChanged<String> get onKeywordChanged =>
|
||||
(value) => _controller
|
||||
..enableClear.value = value.isNotEmpty
|
||||
..onRefresh().whenComplete(
|
||||
() => WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => widget.scrollController?.jumpToTop(),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
@@ -113,7 +114,7 @@ class _DynMentionPanelState extends State<DynMentionPanel> {
|
||||
child: TextField(
|
||||
focusNode: _controller.focusNode,
|
||||
controller: _controller.controller,
|
||||
onChanged: _ctr.add,
|
||||
onChanged: ctr!.add,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(
|
||||
gapPadding: 0,
|
||||
|
||||
@@ -8,10 +8,11 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/topic_item.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_select_topic/controller.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_select_topic/widgets/item.dart';
|
||||
import 'package:PiliPlus/pages/search/controller.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
import 'package:get/get.dart' hide ContextExtensionss;
|
||||
|
||||
class SelectTopicPanel extends StatefulWidget {
|
||||
const SelectTopicPanel({
|
||||
@@ -55,10 +56,11 @@ class SelectTopicPanel extends StatefulWidget {
|
||||
State<SelectTopicPanel> createState() => _SelectTopicPanelState();
|
||||
}
|
||||
|
||||
class _SelectTopicPanelState extends State<SelectTopicPanel> {
|
||||
class _SelectTopicPanelState extends State<SelectTopicPanel>
|
||||
with SearchKeywordMixin {
|
||||
final _controller = Get.put(SelectTopicController());
|
||||
final StreamController<String> _ctr = StreamController<String>();
|
||||
late StreamSubscription<String> _sub;
|
||||
@override
|
||||
Duration get duration => const Duration(milliseconds: 300);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -66,26 +68,25 @@ class _SelectTopicPanelState extends State<SelectTopicPanel> {
|
||||
if (_controller.loadingState.value is Error) {
|
||||
_controller.onReload();
|
||||
}
|
||||
_sub = _ctr.stream
|
||||
.debounce(const Duration(milliseconds: 300), trailing: true)
|
||||
.listen((value) {
|
||||
_controller
|
||||
..enableClear.value = value.isNotEmpty
|
||||
..onRefresh().whenComplete(
|
||||
() => WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => widget.scrollController?.jumpToTop(),
|
||||
),
|
||||
);
|
||||
});
|
||||
subInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_sub.cancel();
|
||||
_ctr.close();
|
||||
subDispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
ValueChanged<String> get onKeywordChanged =>
|
||||
(value) => _controller
|
||||
..enableClear.value = value.isNotEmpty
|
||||
..onRefresh().whenComplete(
|
||||
() => WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => widget.scrollController?.jumpToTop(),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
@@ -109,7 +110,7 @@ class _SelectTopicPanelState extends State<SelectTopicPanel> {
|
||||
child: TextField(
|
||||
focusNode: _controller.focusNode,
|
||||
controller: _controller.controller,
|
||||
onChanged: _ctr.add,
|
||||
onChanged: ctr!.add,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(
|
||||
gapPadding: 0,
|
||||
|
||||
@@ -20,7 +20,7 @@ import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart' as pgc;
|
||||
import 'package:PiliPlus/models_new/video/video_detail/episode.dart' as ugc;
|
||||
import 'package:PiliPlus/models_new/video/video_detail/page.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_relation/data.dart';
|
||||
import 'package:PiliPlus/pages/common/common_slide_page.dart';
|
||||
import 'package:PiliPlus/pages/common/slide/common_slide_page.dart';
|
||||
import 'package:PiliPlus/pages/video/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart';
|
||||
@@ -75,7 +75,7 @@ class EpisodePanel extends CommonSlidePage {
|
||||
final int initialTabIndex;
|
||||
final bool? isSupportReverse;
|
||||
final bool? isReversed;
|
||||
final Function onChangeEpisode;
|
||||
final ValueChanged<ugc.BaseEpisodeItem> onChangeEpisode;
|
||||
final VoidCallback? onReverse;
|
||||
final VoidCallback? onClose;
|
||||
|
||||
@@ -330,7 +330,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
|
||||
|
||||
Widget _buildEpisodeItem({
|
||||
required ThemeData theme,
|
||||
required dynamic episode,
|
||||
required ugc.BaseEpisodeItem episode,
|
||||
required int index,
|
||||
required int length,
|
||||
required bool isCurrentIndex,
|
||||
@@ -367,7 +367,12 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
|
||||
bvid = item.bvid;
|
||||
title = item.showTitle ?? item.title!;
|
||||
cover = item.cover;
|
||||
duration = item.duration == null ? null : item.duration! ~/ 1000;
|
||||
if (item.from == 'pugv') {
|
||||
duration = item.duration;
|
||||
view = item.play;
|
||||
} else {
|
||||
duration = item.duration == null ? null : item.duration! ~/ 1000;
|
||||
}
|
||||
pubdate = item.pubTime;
|
||||
break;
|
||||
}
|
||||
@@ -379,7 +384,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (episode.badge != null && episode.badge == "会员") {
|
||||
if (episode.badge == "会员") {
|
||||
UserInfoData? userInfo = GStorage.userInfo.get('userInfoCache');
|
||||
int vipStatus = userInfo?.vipStatus ?? 0;
|
||||
if (vipStatus != 1) {
|
||||
@@ -392,14 +397,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
|
||||
if (!widget.showTitle) {
|
||||
_currentItemIndex = index;
|
||||
}
|
||||
final isEpisode = episode is ugc.BaseEpisodeItem;
|
||||
widget.onChangeEpisode(
|
||||
episode is pgc.EpisodeItem ? episode.epId : null,
|
||||
isEpisode ? episode.bvid : widget.bvid,
|
||||
episode.cid,
|
||||
isEpisode ? episode.aid : widget.aid,
|
||||
cover,
|
||||
);
|
||||
widget.onChangeEpisode(episode);
|
||||
if (widget.type == EpisodeType.season) {
|
||||
try {
|
||||
Get.find<VideoDetailController>(
|
||||
|
||||
40
lib/pages/fav/cheese/controller.dart
Normal file
40
lib/pages/fav/cheese/controller.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/data.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class FavCheeseController
|
||||
extends CommonListController<SpaceCheeseData, SpaceCheeseItem> {
|
||||
final mid = Accounts.main.mid;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
List<SpaceCheeseItem>? getDataList(SpaceCheeseData response) {
|
||||
isEnd = response.page?.next == false;
|
||||
return response.items;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<SpaceCheeseData>> customGetData() =>
|
||||
FavHttp.favPugv(mid: mid, page: page);
|
||||
|
||||
Future<void> onRemove(int index, int? sid) async {
|
||||
var res = await FavHttp.delFavPugv(sid);
|
||||
if (res['status']) {
|
||||
loadingState
|
||||
..value.data!.removeAt(index)
|
||||
..refresh();
|
||||
SmartDialog.showToast('已取消收藏');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
lib/pages/fav/cheese/view.dart
Normal file
87
lib/pages/fav/cheese/view.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/item.dart';
|
||||
import 'package:PiliPlus/pages/fav/cheese/controller.dart';
|
||||
import 'package:PiliPlus/pages/member_cheese/widgets/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FavCheesePage extends StatefulWidget {
|
||||
const FavCheesePage({super.key});
|
||||
|
||||
@override
|
||||
State<FavCheesePage> createState() => _FavCheesePageState();
|
||||
}
|
||||
|
||||
class _FavCheesePageState extends State<FavCheesePage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
final FavCheeseController _controller = Get.put(FavCheeseController());
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final ThemeData theme = Theme.of(context);
|
||||
return refreshIndicator(
|
||||
onRefresh: _controller.onRefresh,
|
||||
child: CustomScrollView(
|
||||
controller: _controller.scrollController,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 7,
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: Obx(
|
||||
() => _buildBody(theme, _controller.loadingState.value),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(
|
||||
ThemeData theme,
|
||||
LoadingState<List<SpaceCheeseItem>?> loadingState,
|
||||
) {
|
||||
return switch (loadingState) {
|
||||
Loading() => linearLoading,
|
||||
Success(:var response) =>
|
||||
response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: Grid.videoCardHDelegate(context),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == response.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
final item = response[index];
|
||||
return MemberCheeseItem(
|
||||
item: item,
|
||||
onRemove: () => showConfirmDialog(
|
||||
context: context,
|
||||
title: '确定取消收藏该课堂?',
|
||||
onConfirm: () =>
|
||||
_controller.onRemove(index, item.seasonId),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: response!.length,
|
||||
),
|
||||
)
|
||||
: HttpError(onReload: _controller.onReload),
|
||||
Error(:var errMsg) => HttpError(
|
||||
errMsg: errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
onPressed: () {
|
||||
if (_favNoteController.checkedCount.value != 0) {
|
||||
if (_favNoteController.checkedCount != 0) {
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
title: '确定删除已选中的笔记吗?',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_note/list.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/multi_select_controller.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class FavNoteController
|
||||
@@ -34,24 +34,17 @@ class FavNoteController
|
||||
: FavHttp.noteList(page: page);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onRemove() async {
|
||||
List<FavNoteItemModel> dataList = loadingState.value.data!;
|
||||
Set<FavNoteItemModel> removeList = dataList
|
||||
.where((item) => item.checked == true)
|
||||
.toSet();
|
||||
final removeList = allChecked.toSet();
|
||||
final res = await FavHttp.delNote(
|
||||
isPublish: isPublish,
|
||||
noteIds: removeList
|
||||
.map((item) => isPublish ? item.cvid : item.noteId)
|
||||
.toList(),
|
||||
.join(','),
|
||||
);
|
||||
if (res['status']) {
|
||||
List<FavNoteItemModel> remainList = dataList
|
||||
.toSet()
|
||||
.difference(removeList)
|
||||
.toList();
|
||||
loadingState.value = Success(remainList);
|
||||
enableMultiSelect.value = false;
|
||||
afterDelete(removeList);
|
||||
SmartDialog.showToast('删除成功');
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
@@ -59,7 +52,7 @@ class FavNoteController
|
||||
}
|
||||
|
||||
void onDisable() {
|
||||
if (checkedCount.value != 0) {
|
||||
if (checkedCount != 0) {
|
||||
handleSelect();
|
||||
}
|
||||
enableMultiSelect.value = false;
|
||||
|
||||
@@ -128,8 +128,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (_favPgcController.checkedCount.value !=
|
||||
0) {
|
||||
if (_favPgcController.checkedCount != 0) {
|
||||
_favPgcController.onUpdateList(
|
||||
item.followStatus,
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/data.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/multi_select_controller.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -48,7 +48,7 @@ class FavPgcController
|
||||
);
|
||||
|
||||
void onDisable() {
|
||||
if (checkedCount.value != 0) {
|
||||
if (checkedCount != 0) {
|
||||
handleSelect();
|
||||
}
|
||||
enableMultiSelect.value = false;
|
||||
@@ -65,33 +65,30 @@ class FavPgcController
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
assert(false, 'call onUpdateList');
|
||||
}
|
||||
|
||||
Future<void> onUpdateList(int followStatus) async {
|
||||
List<FavPgcItemModel> dataList = loadingState.value.data!;
|
||||
Set<FavPgcItemModel> updateList = dataList
|
||||
.where((item) => item.checked == true)
|
||||
.toSet();
|
||||
final removeList = allChecked.toSet();
|
||||
final res = await VideoHttp.pgcUpdate(
|
||||
seasonId: updateList.map((item) => item.seasonId).toList(),
|
||||
seasonId: removeList.map((item) => item.seasonId).join(','),
|
||||
status: followStatus,
|
||||
);
|
||||
if (res['status']) {
|
||||
List<FavPgcItemModel> remainList = dataList
|
||||
.toSet()
|
||||
.difference(updateList)
|
||||
.toList();
|
||||
loadingState.value = Success(remainList);
|
||||
enableMultiSelect.value = false;
|
||||
try {
|
||||
final ctr = Get.find<FavPgcController>(tag: '$type$followStatus');
|
||||
if (ctr.loadingState.value.isSuccess) {
|
||||
ctr.loadingState
|
||||
..value.data!.insertAll(
|
||||
0,
|
||||
updateList.map((item) => item..checked = null),
|
||||
removeList.map((item) => item..checked = null),
|
||||
)
|
||||
..refresh();
|
||||
ctr.allSelected.value = false;
|
||||
}
|
||||
afterDelete(removeList);
|
||||
} catch (e) {
|
||||
if (kDebugMode) debugPrint('fav pgc onUpdate: $e');
|
||||
}
|
||||
@@ -101,7 +98,7 @@ class FavPgcController
|
||||
|
||||
Future<void> onUpdate(int index, int followStatus, int? seasonId) async {
|
||||
var result = await VideoHttp.pgcUpdate(
|
||||
seasonId: [seasonId],
|
||||
seasonId: seasonId.toString(),
|
||||
status: followStatus,
|
||||
);
|
||||
if (result['status']) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -18,7 +18,7 @@ class FavPgcItem extends StatelessWidget {
|
||||
});
|
||||
|
||||
final FavPgcItemModel item;
|
||||
final MultiSelectController ctr;
|
||||
final MultiSelectBase ctr;
|
||||
final VoidCallback onSelect;
|
||||
final VoidCallback onUpdateStatus;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/fav_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
||||
import 'package:PiliPlus/pages/fav/article/controller.dart';
|
||||
import 'package:PiliPlus/pages/fav/cheese/controller.dart';
|
||||
import 'package:PiliPlus/pages/fav/topic/controller.dart';
|
||||
import 'package:PiliPlus/pages/fav/video/controller.dart';
|
||||
import 'package:PiliPlus/pages/fav_folder_sort/view.dart';
|
||||
@@ -141,6 +142,9 @@ class _FavPageState extends State<FavPage> with SingleTickerProviderStateMixin {
|
||||
.animToTop();
|
||||
case FavTabType.topic:
|
||||
Get.find<FavTopicController>().scrollController.animToTop();
|
||||
case FavTabType.cheese:
|
||||
Get.find<FavCheeseController>().scrollController
|
||||
.animToTop();
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/fav_order_type.dart';
|
||||
@@ -5,24 +6,83 @@ import 'package:PiliPlus/models/common/video/source_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/data.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/media.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/multi_select_controller.dart';
|
||||
import 'package:PiliPlus/pages/fav_sort/view.dart';
|
||||
import 'package:PiliPlus/services/account_service.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show ValueChanged;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
mixin BaseFavController
|
||||
on
|
||||
CommonListController<FavDetailData, FavDetailItemModel>,
|
||||
DeleteItemMixin<FavDetailData, FavDetailItemModel> {
|
||||
bool get isOwner;
|
||||
int get mediaId;
|
||||
|
||||
ValueChanged<int>? updateCount;
|
||||
|
||||
void onViewFav(FavDetailItemModel item, int? index);
|
||||
|
||||
Future<void> onCancelFav(int index, int id, int type) async {
|
||||
var result = await FavHttp.favVideo(
|
||||
resources: '$id:$type',
|
||||
delIds: mediaId.toString(),
|
||||
);
|
||||
if (result['status']) {
|
||||
loadingState
|
||||
..value.data!.removeAt(index)
|
||||
..refresh();
|
||||
updateCount?.call(1);
|
||||
SmartDialog.showToast('取消收藏');
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
showConfirmDialog(
|
||||
context: Get.context!,
|
||||
content: '确认删除所选收藏吗?',
|
||||
title: '提示',
|
||||
onConfirm: () async {
|
||||
final removeList = allChecked.toSet();
|
||||
var result = await FavHttp.favVideo(
|
||||
resources: removeList
|
||||
.map((item) => '${item.id}:${item.type}')
|
||||
.join(','),
|
||||
delIds: mediaId.toString(),
|
||||
);
|
||||
if (result['status']) {
|
||||
updateCount?.call(removeList.length);
|
||||
afterDelete(removeList);
|
||||
SmartDialog.showToast('取消收藏');
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FavDetailController
|
||||
extends MultiSelectController<FavDetailData, FavDetailItemModel> {
|
||||
extends MultiSelectController<FavDetailData, FavDetailItemModel>
|
||||
with BaseFavController {
|
||||
@override
|
||||
late int mediaId;
|
||||
late String heroTag;
|
||||
final Rx<FavFolderInfo> folderInfo = FavFolderInfo().obs;
|
||||
final Rx<bool?> isOwner = Rx<bool?>(null);
|
||||
final Rx<bool?> _isOwner = Rx<bool?>(null);
|
||||
final Rx<FavOrderType> order = FavOrderType.mtime.obs;
|
||||
|
||||
@override
|
||||
bool get isOwner => _isOwner.value ?? false;
|
||||
|
||||
AccountService accountService = Get.find<AccountService>();
|
||||
|
||||
@override
|
||||
@@ -58,28 +118,16 @@ class FavDetailController
|
||||
if (isRefresh) {
|
||||
FavDetailData data = response.response;
|
||||
folderInfo.value = data.info!;
|
||||
isOwner.value = data.info?.mid == accountService.mid;
|
||||
_isOwner.value = data.info?.mid == accountService.mid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> onCancelFav(int index, int id, int type) async {
|
||||
var result = await FavHttp.favVideo(
|
||||
resources: '$id:$type',
|
||||
delIds: mediaId.toString(),
|
||||
);
|
||||
if (result['status']) {
|
||||
folderInfo
|
||||
..value.mediaCount -= 1
|
||||
@override
|
||||
ValueChanged<int>? get updateCount =>
|
||||
(count) => folderInfo
|
||||
..value.mediaCount -= count
|
||||
..refresh();
|
||||
loadingState
|
||||
..value.data!.removeAt(index)
|
||||
..refresh();
|
||||
SmartDialog.showToast('取消收藏');
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<FavDetailData>> customGetData() =>
|
||||
@@ -90,64 +138,6 @@ class FavDetailController
|
||||
order: order.value,
|
||||
);
|
||||
|
||||
void onDelChecked(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('提示'),
|
||||
content: const Text('确认删除所选收藏吗?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
List<FavDetailItemModel> list = loadingState.value.data!
|
||||
.where((e) => e.checked == true)
|
||||
.toList();
|
||||
var result = await FavHttp.favVideo(
|
||||
resources: list
|
||||
.map((item) => '${item.id}:${item.type}')
|
||||
.join(','),
|
||||
delIds: mediaId.toString(),
|
||||
);
|
||||
if (result['status']) {
|
||||
List<FavDetailItemModel> dataList = loadingState.value.data!;
|
||||
List<FavDetailItemModel> remainList = dataList
|
||||
.toSet()
|
||||
.difference(list.toSet())
|
||||
.toList();
|
||||
folderInfo
|
||||
..value.mediaCount -= list.length
|
||||
..refresh();
|
||||
if (remainList.isNotEmpty) {
|
||||
loadingState.value = Success(remainList);
|
||||
} else {
|
||||
onReload();
|
||||
}
|
||||
SmartDialog.showToast('取消收藏');
|
||||
checkedCount.value = 0;
|
||||
enableMultiSelect.value = false;
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
},
|
||||
child: const Text('确认'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void toViewPlayAll() {
|
||||
if (loadingState.value.isSuccess) {
|
||||
List<FavDetailItemModel>? list = loadingState.value.data;
|
||||
@@ -157,24 +147,7 @@ class FavDetailController
|
||||
if (element.ugc?.firstCid == null) {
|
||||
continue;
|
||||
} else {
|
||||
if (element.bvid != list.first.bvid) {
|
||||
SmartDialog.showToast('已跳过不支持播放的视频');
|
||||
}
|
||||
final folderInfo = this.folderInfo.value;
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=${element.bvid}&cid=${element.ugc!.firstCid}',
|
||||
arguments: {
|
||||
'videoItem': element,
|
||||
'heroTag': Utils.makeHeroTag(element.bvid),
|
||||
'sourceType': SourceType.fav,
|
||||
'mediaId': folderInfo.id,
|
||||
'oid': element.id,
|
||||
'favTitle': folderInfo.title,
|
||||
'count': folderInfo.mediaCount,
|
||||
'desc': true,
|
||||
'isOwner': isOwner.value ?? false,
|
||||
},
|
||||
);
|
||||
onViewFav(element, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -224,4 +197,25 @@ class FavDetailController
|
||||
Get.to(FavSortPage(favDetailController: this));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onViewFav(FavDetailItemModel item, int? index) {
|
||||
final folder = folderInfo.value;
|
||||
PageUtils.toVideoPage(
|
||||
bvid: item.bvid,
|
||||
cid: item.ugc!.firstCid!,
|
||||
cover: item.cover,
|
||||
title: item.title,
|
||||
extraArguments: {
|
||||
'sourceType': SourceType.fav,
|
||||
'mediaId': folder.id,
|
||||
'oid': item.id,
|
||||
'favTitle': folder.title,
|
||||
'count': folder.mediaCount,
|
||||
'desc': true,
|
||||
if (index != null) 'isContinuePlaying': index != 0,
|
||||
'isOwner': isOwner,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
@@ -8,7 +7,6 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/fav_order_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/source_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/data.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/media.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
||||
@@ -17,7 +15,6 @@ import 'package:PiliPlus/pages/fav_detail/controller.dart';
|
||||
import 'package:PiliPlus/pages/fav_detail/widget/fav_video_card.dart';
|
||||
import 'package:PiliPlus/utils/fav_util.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -113,7 +110,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
Obx(
|
||||
() {
|
||||
return Text(
|
||||
'已选: ${_favDetailController.checkedCount.value}',
|
||||
'已选: ${_favDetailController.checkedCount}',
|
||||
style: const TextStyle(fontSize: 15),
|
||||
);
|
||||
},
|
||||
@@ -156,7 +153,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
'mediaId': int.parse(mediaId),
|
||||
'title': folderInfo.title,
|
||||
'count': folderInfo.mediaCount,
|
||||
'isOwner': _favDetailController.isOwner.value ?? false,
|
||||
'isOwner': _favDetailController.isOwner,
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -198,7 +195,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
PopupMenuButton(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) {
|
||||
final isOwner = _favDetailController.isOwner.value ?? false;
|
||||
final isOwner = _favDetailController.isOwner;
|
||||
final folderInfo = _favDetailController.folderInfo.value;
|
||||
return [
|
||||
if (isOwner) ...[
|
||||
@@ -252,7 +249,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
context: context,
|
||||
title: '确定删除该收藏夹?',
|
||||
onConfirm: () =>
|
||||
FavHttp.deleteFolder(mediaIds: [mediaId]).then((res) {
|
||||
FavHttp.deleteFolder(mediaIds: mediaId).then((res) {
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast('删除成功');
|
||||
Get.back(result: true);
|
||||
@@ -327,7 +324,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: () => _favDetailController.onDelChecked(context),
|
||||
onPressed: _favDetailController.onRemove,
|
||||
child: Text(
|
||||
'删除',
|
||||
style: TextStyle(color: theme.colorScheme.error),
|
||||
@@ -373,7 +370,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
right: 6,
|
||||
top: 6,
|
||||
child: Obx(() {
|
||||
if (_favDetailController.isOwner.value != false) {
|
||||
if (_favDetailController.isOwner) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
bool isFav = folderInfo.favState == 1;
|
||||
@@ -485,112 +482,11 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
final isOwner = _favDetailController.isOwner.value ?? false;
|
||||
FavDetailItemModel item = response[index];
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: FavVideoCardH(
|
||||
item: item,
|
||||
onDelFav: isOwner
|
||||
? () => _favDetailController.onCancelFav(
|
||||
index,
|
||||
item.id!,
|
||||
item.type!,
|
||||
)
|
||||
: null,
|
||||
onViewFav: () {
|
||||
final folderInfo =
|
||||
_favDetailController.folderInfo.value;
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=${item.bvid}&cid=${item.ugc?.firstCid}',
|
||||
arguments: {
|
||||
'videoItem': item,
|
||||
'heroTag': Utils.makeHeroTag(item.bvid),
|
||||
'sourceType': SourceType.fav,
|
||||
'mediaId': folderInfo.id,
|
||||
'oid': item.id,
|
||||
'favTitle': folderInfo.title,
|
||||
'count': folderInfo.mediaCount,
|
||||
'desc': true,
|
||||
'isContinuePlaying': index != 0,
|
||||
'isOwner': isOwner,
|
||||
},
|
||||
);
|
||||
},
|
||||
onTap: enableMultiSelect
|
||||
? () => _favDetailController.onSelect(item)
|
||||
: null,
|
||||
onLongPress: isOwner
|
||||
? () {
|
||||
if (!enableMultiSelect) {
|
||||
_favDetailController
|
||||
.enableMultiSelect
|
||||
.value =
|
||||
true;
|
||||
_favDetailController.onSelect(item);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 5,
|
||||
left: 12,
|
||||
bottom: 5,
|
||||
child: IgnorePointer(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
AnimatedOpacity(
|
||||
opacity: item.checked == true ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: constraints.maxHeight,
|
||||
width:
|
||||
constraints.maxHeight *
|
||||
StyleString.aspectRatio,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: StyleString.mdRadius,
|
||||
color: Colors.black.withValues(
|
||||
alpha: 0.6,
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 34,
|
||||
height: 34,
|
||||
child: AnimatedScale(
|
||||
scale: item.checked == true ? 1 : 0,
|
||||
duration: const Duration(
|
||||
milliseconds: 250,
|
||||
),
|
||||
curve: Curves.easeInOut,
|
||||
child: IconButton(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
EdgeInsets.zero,
|
||||
),
|
||||
backgroundColor:
|
||||
WidgetStatePropertyAll(
|
||||
theme.colorScheme.surface
|
||||
.withValues(alpha: 0.8),
|
||||
),
|
||||
),
|
||||
onPressed: null,
|
||||
icon: Icon(
|
||||
Icons.done_all_outlined,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
return FavVideoCardH(
|
||||
item: item,
|
||||
index: index,
|
||||
ctr: _favDetailController,
|
||||
);
|
||||
},
|
||||
childCount: response!.length + 1,
|
||||
|
||||
@@ -3,10 +3,12 @@ import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/media.dart';
|
||||
import 'package:PiliPlus/pages/fav_detail/controller.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/duration_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
@@ -16,55 +18,60 @@ import 'package:get/get.dart';
|
||||
// 收藏视频卡片 - 水平布局
|
||||
class FavVideoCardH extends StatelessWidget {
|
||||
final FavDetailItemModel item;
|
||||
final GestureTapCallback? onTap;
|
||||
final GestureLongPressCallback? onLongPress;
|
||||
final VoidCallback? onDelFav;
|
||||
final VoidCallback? onViewFav;
|
||||
final bool? isSort;
|
||||
final int? index;
|
||||
final BaseFavController? ctr;
|
||||
|
||||
const FavVideoCardH({
|
||||
super.key,
|
||||
required this.item,
|
||||
this.onDelFav,
|
||||
this.onTap,
|
||||
this.onLongPress,
|
||||
this.onViewFav,
|
||||
this.isSort,
|
||||
});
|
||||
this.index,
|
||||
this.ctr,
|
||||
}) : assert(ctr == null || index != null);
|
||||
|
||||
bool get isSort => ctr == null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isOwner = !isSort && ctr!.isOwner;
|
||||
late final enableMultiSelect = ctr?.enableMultiSelect.value ?? false;
|
||||
final theme = Theme.of(context);
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
onTap: isSort == true
|
||||
onTap: isSort
|
||||
? null
|
||||
: onTap ??
|
||||
() {
|
||||
if (!const [0, 16].contains(item.attr)) {
|
||||
Get.toNamed('/member?mid=${item.upper?.mid}');
|
||||
return;
|
||||
}
|
||||
: enableMultiSelect
|
||||
? () => ctr!.onSelect(item)
|
||||
: () {
|
||||
if (!const [0, 16].contains(item.attr)) {
|
||||
Get.toNamed('/member?mid=${item.upper?.mid}');
|
||||
return;
|
||||
}
|
||||
|
||||
// pgc
|
||||
if (item.type == 24) {
|
||||
PageUtils.viewPgc(
|
||||
seasonId: item.ogv!.seasonId,
|
||||
epId: item.id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// pgc
|
||||
if (item.type == 24) {
|
||||
PageUtils.viewPgc(
|
||||
seasonId: item.ogv!.seasonId,
|
||||
epId: item.id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
onViewFav?.call();
|
||||
},
|
||||
onLongPress: isSort == true
|
||||
ctr!.onViewFav(item, index);
|
||||
},
|
||||
onLongPress: isSort
|
||||
? null
|
||||
: onLongPress ??
|
||||
() => imageSaveDialog(
|
||||
title: item.title,
|
||||
cover: item.cover,
|
||||
bvid: item.bvid,
|
||||
),
|
||||
: isOwner && !enableMultiSelect
|
||||
? () {
|
||||
ctr!
|
||||
..enableMultiSelect.value = true
|
||||
..onSelect(item);
|
||||
}
|
||||
: () => imageSaveDialog(
|
||||
title: item.title,
|
||||
cover: item.cover,
|
||||
bvid: item.bvid,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
@@ -100,13 +107,20 @@ class FavVideoCardH extends StatelessWidget {
|
||||
bottom: null,
|
||||
left: null,
|
||||
),
|
||||
if (!isSort)
|
||||
Positioned.fill(
|
||||
child: selectMask(
|
||||
theme,
|
||||
item.checked == true,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
content(context),
|
||||
content(context, theme, isOwner),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -114,8 +128,7 @@ class FavVideoCardH extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget content(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
Widget content(BuildContext context, ThemeData theme, isOwner) {
|
||||
return Expanded(
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
@@ -170,7 +183,7 @@ class FavVideoCardH extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (onDelFav != null)
|
||||
if (isOwner)
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: -8,
|
||||
@@ -197,7 +210,7 @@ class FavVideoCardH extends StatelessWidget {
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
onDelFav!();
|
||||
ctr!.onCancelFav(index!, item.id!, item.type!);
|
||||
},
|
||||
child: const Text('确定取消'),
|
||||
),
|
||||
|
||||
@@ -33,7 +33,7 @@ class _FavFolderSortPageState extends State<FavFolderSortPage> {
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
var res = await FavHttp.sortFavFolder(
|
||||
sort: sortList.map((item) => item.id).toList(),
|
||||
sort: sortList.map((item) => item.id).join(','),
|
||||
);
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast('排序完成');
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
import 'package:PiliPlus/http/fav.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/fav_order_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/source_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/data.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/media.dart';
|
||||
import 'package:PiliPlus/pages/common/common_search_controller.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
import 'package:PiliPlus/pages/common/search/common_search_controller.dart';
|
||||
import 'package:PiliPlus/pages/fav_detail/controller.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FavSearchController
|
||||
extends CommonSearchController<FavDetailData, FavDetailItemModel> {
|
||||
extends CommonSearchController<FavDetailData, FavDetailItemModel>
|
||||
with
|
||||
CommonMultiSelectMixin<FavDetailItemModel>,
|
||||
DeleteItemMixin,
|
||||
BaseFavController {
|
||||
int type = Get.arguments['type'];
|
||||
@override
|
||||
int mediaId = Get.arguments['mediaId'];
|
||||
@override
|
||||
bool isOwner = Get.arguments['isOwner'];
|
||||
dynamic count = Get.arguments['count'];
|
||||
dynamic title = Get.arguments['title'];
|
||||
@@ -36,17 +45,20 @@ class FavSearchController
|
||||
return response.medias;
|
||||
}
|
||||
|
||||
Future<void> onCancelFav(int index, int id, int? type) async {
|
||||
var result = await FavHttp.favVideo(
|
||||
resources: '$id:$type',
|
||||
addIds: '',
|
||||
delIds: mediaId.toString(),
|
||||
);
|
||||
if (result['status']) {
|
||||
loadingState
|
||||
..value.data!.removeAt(index)
|
||||
..refresh();
|
||||
SmartDialog.showToast('取消收藏');
|
||||
}
|
||||
}
|
||||
@override
|
||||
void onViewFav(FavDetailItemModel item, int? index) => PageUtils.toVideoPage(
|
||||
bvid: item.bvid,
|
||||
cid: item.ugc!.firstCid!,
|
||||
cover: item.cover,
|
||||
title: item.title,
|
||||
extraArguments: {
|
||||
'sourceType': SourceType.fav,
|
||||
'mediaId': mediaId,
|
||||
'oid': item.id,
|
||||
'favTitle': title,
|
||||
'count': count,
|
||||
'desc': true,
|
||||
'isContinuePlaying': true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import 'package:PiliPlus/models/common/fav_order_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/source_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/data.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_detail/media.dart';
|
||||
import 'package:PiliPlus/pages/common/common_search_page.dart';
|
||||
import 'package:PiliPlus/pages/common/search/common_search_page.dart';
|
||||
import 'package:PiliPlus/pages/fav_detail/widget/fav_video_card.dart';
|
||||
import 'package:PiliPlus/pages/fav_search/controller.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -69,27 +67,8 @@ class _FavSearchPageState
|
||||
final item = list[index];
|
||||
return FavVideoCardH(
|
||||
item: item,
|
||||
onDelFav: controller.isOwner == true
|
||||
? () => controller.onCancelFav(
|
||||
index,
|
||||
item.id!,
|
||||
item.type,
|
||||
)
|
||||
: null,
|
||||
onViewFav: () => PageUtils.toVideoPage(
|
||||
'bvid=${item.bvid}&cid=${item.ugc?.firstCid}',
|
||||
arguments: {
|
||||
'videoItem': item,
|
||||
'heroTag': Utils.makeHeroTag(item.bvid),
|
||||
'sourceType': SourceType.fav,
|
||||
'mediaId': controller.mediaId,
|
||||
'oid': item.id,
|
||||
'favTitle': controller.title,
|
||||
'count': controller.count,
|
||||
'desc': true,
|
||||
'isContinuePlaying': true,
|
||||
},
|
||||
),
|
||||
index: index,
|
||||
ctr: controller,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -79,7 +79,7 @@ class _FavSortPageState extends State<FavSortPage> {
|
||||
}
|
||||
var res = await FavHttp.sortFav(
|
||||
mediaId: _favDetailController.mediaId,
|
||||
sort: sort,
|
||||
sort: sort.join(','),
|
||||
);
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast('排序完成');
|
||||
@@ -136,10 +136,7 @@ class _FavSortPageState extends State<FavSortPage> {
|
||||
return SizedBox(
|
||||
key: Key(item.id.toString()),
|
||||
height: 98,
|
||||
child: FavVideoCardH(
|
||||
isSort: true,
|
||||
item: item,
|
||||
),
|
||||
child: FavVideoCardH(item: item),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -32,11 +32,10 @@ class FollowController extends GetxController with GetTickerProviderStateMixin {
|
||||
|
||||
Future<void> queryFollowUpTags() async {
|
||||
var res = await MemberHttp.followUpTags();
|
||||
if (res['status']) {
|
||||
if (res.isSuccess) {
|
||||
tabs
|
||||
..clear()
|
||||
..addAll(res['data'])
|
||||
..insert(0, MemberTagItemModel(name: '全部关注'));
|
||||
..assign(MemberTagItemModel(name: '全部关注'))
|
||||
..addAll(res.data);
|
||||
int initialIndex = 0;
|
||||
if (tabController != null) {
|
||||
initialIndex = tabController!.index.clamp(0, tabs.length - 1);
|
||||
@@ -49,7 +48,7 @@ class FollowController extends GetxController with GetTickerProviderStateMixin {
|
||||
);
|
||||
followState.value = Success(tabs.hashCode);
|
||||
} else {
|
||||
followState.value = Error(res['msg']);
|
||||
followState.value = res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||
import 'package:PiliPlus/pages/common/common_search_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/search/common_search_controller.dart';
|
||||
|
||||
class FollowSearchController
|
||||
extends CommonSearchController<FollowData, FollowItemModel> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||
import 'package:PiliPlus/pages/common/common_search_page.dart';
|
||||
import 'package:PiliPlus/pages/common/search/common_search_page.dart';
|
||||
import 'package:PiliPlus/pages/follow/widgets/follow_item.dart';
|
||||
import 'package:PiliPlus/pages/follow_search/controller.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
|
||||
@@ -2,18 +2,19 @@ import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/member/tags.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class GroupPanel extends StatefulWidget {
|
||||
final int? mid;
|
||||
final int mid;
|
||||
final List? tags;
|
||||
final ScrollController? scrollController;
|
||||
const GroupPanel({
|
||||
super.key,
|
||||
this.mid,
|
||||
required this.mid,
|
||||
this.tags,
|
||||
this.scrollController,
|
||||
});
|
||||
@@ -23,9 +24,9 @@ class GroupPanel extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupPanelState extends State<GroupPanel> {
|
||||
LoadingState<List<MemberTagItemModel>> loadingState =
|
||||
LoadingState<List<MemberTagItemModel>>.loading();
|
||||
LoadingState<List<MemberTagItemModel>> loadingState = LoadingState.loading();
|
||||
RxBool showDefaultBtn = true.obs;
|
||||
late final Set<int> tags = widget.tags?.cast<int>().toSet() ?? {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -36,19 +37,8 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
void _query() {
|
||||
MemberHttp.followUpTags().then((res) {
|
||||
if (mounted) {
|
||||
if (res['status']) {
|
||||
List<MemberTagItemModel> tagsList =
|
||||
(res['data'] as List<MemberTagItemModel>)
|
||||
..removeWhere((item) => item.tagid == 0)
|
||||
..map((item) {
|
||||
return item.checked =
|
||||
widget.tags?.contains(item.tagid) == true;
|
||||
}).toList();
|
||||
showDefaultBtn.value = !tagsList.any((e) => e.checked == true);
|
||||
loadingState = Success(tagsList);
|
||||
} else {
|
||||
loadingState = Error(res['msg']);
|
||||
}
|
||||
loadingState = res..dataOrNull.removeFirstWhere((e) => e.tagid == 0);
|
||||
showDefaultBtn.value = tags.isEmpty;
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
@@ -60,25 +50,14 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
return;
|
||||
}
|
||||
feedBack();
|
||||
// 是否有选中的 有选中的带id,没选使用默认0
|
||||
List<MemberTagItemModel> tagsList = loadingState.data;
|
||||
final bool anyHasChecked = tagsList.any(
|
||||
(MemberTagItemModel e) => e.checked == true,
|
||||
);
|
||||
late List<int> tagidList;
|
||||
if (anyHasChecked) {
|
||||
final List<MemberTagItemModel> checkedList = tagsList
|
||||
.where((MemberTagItemModel e) => e.checked == true)
|
||||
.toList();
|
||||
tagidList = checkedList.map<int>((e) => e.tagid!).toList();
|
||||
} else {
|
||||
tagidList = [0];
|
||||
}
|
||||
// 保存
|
||||
final res = await MemberHttp.addUsers([widget.mid], tagidList);
|
||||
final res = await MemberHttp.addUsers(
|
||||
widget.mid.toString(),
|
||||
tags.isEmpty ? '0' : tags.join(','),
|
||||
);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
if (res['status']) {
|
||||
Get.back(result: tagidList);
|
||||
Get.back(result: tags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +74,16 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
void onTap() {
|
||||
item.checked = !item.checked!;
|
||||
final tagid = item.tagid!;
|
||||
if (tags.contains(tagid)) {
|
||||
tags.remove(tagid);
|
||||
item.count--;
|
||||
} else {
|
||||
tags.add(tagid);
|
||||
item.count++;
|
||||
}
|
||||
(context as Element).markNeedsBuild();
|
||||
showDefaultBtn.value = !response.any(
|
||||
(e) => e.checked == true,
|
||||
);
|
||||
showDefaultBtn.value = tags.isEmpty;
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
@@ -107,15 +91,15 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
dense: true,
|
||||
leading: const Icon(Icons.group_outlined),
|
||||
minLeadingWidth: 0,
|
||||
title: Text(item.name ?? ''),
|
||||
title: Text('${item.name} (${item.count})'),
|
||||
subtitle: item.tip?.isNotEmpty == true
|
||||
? Text(item.tip!)
|
||||
: null,
|
||||
trailing: Transform.scale(
|
||||
scale: 0.9,
|
||||
child: Checkbox(
|
||||
value: item.checked,
|
||||
onChanged: (bool? checkValue) => onTap(),
|
||||
value: tags.contains(item.tagid),
|
||||
onChanged: (_) => onTap(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user