Compare commits
14 Commits
fix-routeQ
...
fix-relate
Author | SHA1 | Date | |
---|---|---|---|
7a78729a44 | |||
03e5e22fef | |||
aa93ce0b89 | |||
0c365ad049 | |||
3d5c578fef | |||
0a22f0f543 | |||
5bf7b69d79 | |||
77477ff4dd | |||
b0c56feef5 | |||
191472d0c4 | |||
40c666e3d1 | |||
10d2995429 | |||
23c8b34189 | |||
932be48125 |
@ -22,20 +22,27 @@ class HttpError extends StatelessWidget {
|
||||
"assets/images/error.svg",
|
||||
height: 200,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
errMsg ?? '请求异常',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
OutlinedButton.icon(
|
||||
const SizedBox(height: 20),
|
||||
FilledButton.tonal(
|
||||
onPressed: () {
|
||||
fn!();
|
||||
},
|
||||
icon: const Icon(Icons.arrow_forward_outlined, size: 20),
|
||||
label: Text(btnText ?? '点击重试'),
|
||||
)
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.resolveWith((states) {
|
||||
return Theme.of(context).colorScheme.primary.withAlpha(20);
|
||||
}),
|
||||
),
|
||||
child: Text(
|
||||
btnText ?? '点击重试',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class StatDanMu extends StatelessWidget {
|
||||
final String? theme;
|
||||
final int? danmu;
|
||||
final dynamic danmu;
|
||||
final String? size;
|
||||
|
||||
const StatDanMu({Key? key, this.theme, this.danmu, this.size})
|
||||
|
@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class StatView extends StatelessWidget {
|
||||
final String? theme;
|
||||
final int? view;
|
||||
final dynamic view;
|
||||
final String? size;
|
||||
|
||||
const StatView({Key? key, this.theme, this.view, this.size})
|
||||
|
@ -1,6 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../models/model_rec_video_item.dart';
|
||||
import 'stat/danmu.dart';
|
||||
import 'stat/view.dart';
|
||||
import '../../http/dynamics.dart';
|
||||
import '../../http/search.dart';
|
||||
import '../../http/user.dart';
|
||||
@ -322,19 +325,31 @@ class VideoStat extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RichText(
|
||||
maxLines: 1,
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: MediaQuery.textScalerOf(context)
|
||||
.scale(Theme.of(context).textTheme.labelSmall!.fontSize!),
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
return Row(
|
||||
children: [
|
||||
StatView(
|
||||
theme: 'gray',
|
||||
view: videoItem.stat.view,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: '${Utils.numFormat(videoItem.stat.view)}观看'),
|
||||
TextSpan(text: ' • ${Utils.numFormat(videoItem.stat.danmu)}弹幕'),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
StatDanMu(
|
||||
theme: 'gray',
|
||||
danmu: videoItem.stat.danmu,
|
||||
),
|
||||
if (videoItem is RecVideoItemModel) ...<Widget>[
|
||||
const Spacer(),
|
||||
RichText(
|
||||
maxLines: 1,
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
]
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class VideoHttp {
|
||||
}
|
||||
return {'status': true, 'data': list};
|
||||
} else {
|
||||
return {'status': false, 'data': []};
|
||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||
}
|
||||
} catch (err) {
|
||||
return {'status': false, 'data': [], 'msg': err};
|
||||
|
@ -9,7 +9,6 @@ import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/pages/home/index.dart';
|
||||
import 'package:pilipala/pages/main/index.dart';
|
||||
import 'package:pilipala/pages/rcmd/view.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
import 'widgets/bangumu_card_v.dart';
|
||||
@ -199,7 +198,10 @@ class _BangumiPageState extends State<BangumiPage>
|
||||
} else {
|
||||
return HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => {},
|
||||
fn: () {
|
||||
_futureBuilderFuture =
|
||||
_bangumidController.queryBangumiListFeed();
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -208,7 +210,6 @@ class _BangumiPageState extends State<BangumiPage>
|
||||
},
|
||||
),
|
||||
),
|
||||
const LoadingMore()
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -192,22 +192,6 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
)
|
||||
],
|
||||
),
|
||||
// Obx(
|
||||
// () => Visibility(
|
||||
// visible: _dynamicsController.userLogin.value,
|
||||
// child: Positioned(
|
||||
// right: 4,
|
||||
// top: 0,
|
||||
// bottom: 0,
|
||||
// child: IconButton(
|
||||
// padding: EdgeInsets.zero,
|
||||
// onPressed: () =>
|
||||
// {feedBack(), _dynamicsController.resetSearch()},
|
||||
// icon: const Icon(Icons.history, size: 21),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -229,7 +213,8 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
return Obx(() => UpPanel(_dynamicsController.upData.value));
|
||||
} else {
|
||||
return const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 80));
|
||||
child: SizedBox(height: 80),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return const SliverToBoxAdapter(
|
||||
@ -240,15 +225,6 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
}
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: 6,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
|
@ -36,8 +36,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
}
|
||||
upList.insert(
|
||||
0,
|
||||
UpItem(
|
||||
face: 'https://files.catbox.moe/8uc48f.png', uname: '全部动态', mid: -1),
|
||||
UpItem(face: '', uname: '全部动态', mid: -1),
|
||||
);
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
upList.insert(
|
||||
@ -56,7 +55,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
floating: true,
|
||||
pinned: false,
|
||||
delegate: _SliverHeaderDelegate(
|
||||
height: 124,
|
||||
height: 126,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -121,6 +120,13 @@ class _UpPanelState extends State<UpPanel> {
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 6,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
@ -171,6 +177,9 @@ class _UpPanelState extends State<UpPanel> {
|
||||
},
|
||||
onLongPress: () {
|
||||
feedBack();
|
||||
if (data.mid == -1) {
|
||||
return;
|
||||
}
|
||||
String heroTag = Utils.makeHeroTag(data.mid);
|
||||
Get.toNamed('/member?mid=${data.mid}',
|
||||
arguments: {'face': data.face, 'heroTag': heroTag});
|
||||
@ -198,12 +207,19 @@ class _UpPanelState extends State<UpPanel> {
|
||||
backgroundColor: data.type == 'live'
|
||||
? Theme.of(context).colorScheme.secondaryContainer
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
child: NetworkImgLayer(
|
||||
width: 49,
|
||||
height: 49,
|
||||
src: data.face,
|
||||
type: 'avatar',
|
||||
),
|
||||
child: data.face != ''
|
||||
? NetworkImgLayer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
src: data.face,
|
||||
type: 'avatar',
|
||||
)
|
||||
: const CircleAvatar(
|
||||
radius: 25,
|
||||
backgroundImage: AssetImage(
|
||||
'assets/images/noface.jpeg',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
@ -271,13 +287,11 @@ class UpPanelSkeleton extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 49,
|
||||
height: 49,
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
|
@ -15,9 +15,14 @@ import '../../../common/widgets/badge.dart';
|
||||
class FavVideoCardH extends StatelessWidget {
|
||||
final dynamic videoItem;
|
||||
final Function? callFn;
|
||||
final int? searchType;
|
||||
|
||||
const FavVideoCardH({Key? key, required this.videoItem, this.callFn})
|
||||
: super(key: key);
|
||||
const FavVideoCardH({
|
||||
Key? key,
|
||||
required this.videoItem,
|
||||
this.callFn,
|
||||
this.searchType,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -107,7 +112,11 @@ class FavVideoCardH extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
VideoContent(videoItem: videoItem, callFn: callFn)
|
||||
VideoContent(
|
||||
videoItem: videoItem,
|
||||
callFn: callFn,
|
||||
searchType: searchType,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -123,7 +132,13 @@ class FavVideoCardH extends StatelessWidget {
|
||||
class VideoContent extends StatelessWidget {
|
||||
final dynamic videoItem;
|
||||
final Function? callFn;
|
||||
const VideoContent({super.key, required this.videoItem, this.callFn});
|
||||
final int? searchType;
|
||||
const VideoContent({
|
||||
super.key,
|
||||
required this.videoItem,
|
||||
this.callFn,
|
||||
this.searchType,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -189,48 +204,51 @@ class VideoContent extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: -4,
|
||||
child: IconButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: Get.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 {
|
||||
await callFn!();
|
||||
Get.back();
|
||||
},
|
||||
child: const Text('确定取消'),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.clear_outlined,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
searchType != 1
|
||||
? Positioned(
|
||||
right: 0,
|
||||
bottom: -4,
|
||||
child: IconButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: Get.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 {
|
||||
await callFn!();
|
||||
Get.back();
|
||||
},
|
||||
child: const Text('确定取消'),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.clear_outlined,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,8 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/user.dart';
|
||||
import 'package:pilipala/models/user/fav_detail.dart';
|
||||
|
||||
import '../../http/video.dart';
|
||||
|
||||
class FavSearchController extends GetxController {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
Rx<TextEditingController> controller = TextEditingController().obs;
|
||||
@ -72,4 +75,21 @@ class FavSearchController extends GetxController {
|
||||
if (!hasMore) return;
|
||||
searchFav(type: 'onLoad');
|
||||
}
|
||||
|
||||
onCancelFav(int id) async {
|
||||
var result = await VideoHttp.favVideo(
|
||||
aid: id, addIds: '', delIds: mediaId.toString());
|
||||
if (result['status']) {
|
||||
if (result['data']['prompt']) {
|
||||
List dataList = favList;
|
||||
for (var i in dataList) {
|
||||
if (i.id == id) {
|
||||
dataList.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SmartDialog.showToast('取消收藏');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,7 @@ import 'package:pilipala/pages/fav_detail/widget/fav_video_card.dart';
|
||||
import 'controller.dart';
|
||||
|
||||
class FavSearchPage extends StatefulWidget {
|
||||
final int? sourceType;
|
||||
final int? mediaId;
|
||||
const FavSearchPage({super.key, this.sourceType, this.mediaId});
|
||||
const FavSearchPage({super.key});
|
||||
|
||||
@override
|
||||
State<FavSearchPage> createState() => _FavSearchPageState();
|
||||
@ -19,11 +17,12 @@ class FavSearchPage extends StatefulWidget {
|
||||
class _FavSearchPageState extends State<FavSearchPage> {
|
||||
final FavSearchController _favSearchCtr = Get.put(FavSearchController());
|
||||
late ScrollController scrollController;
|
||||
late int searchType;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
searchType = int.parse(Get.parameters['searchType']!);
|
||||
scrollController = _favSearchCtr.scrollController;
|
||||
scrollController.addListener(
|
||||
() {
|
||||
@ -100,7 +99,11 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
||||
} else {
|
||||
return FavVideoCardH(
|
||||
videoItem: _favSearchCtr.favList[index],
|
||||
callFn: () => null,
|
||||
searchType: searchType,
|
||||
callFn: () => searchType != 1
|
||||
? _favSearchCtr
|
||||
.onCancelFav(_favSearchCtr.favList[index].id!)
|
||||
: {},
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -89,8 +89,7 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
||||
if (data['status']) {
|
||||
return Obx(
|
||||
() => SliverList(
|
||||
delegate:
|
||||
SliverChildBuilderDelegate((context, index) {
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return VideoCardH(
|
||||
videoItem: _hotController.videoList[index],
|
||||
showPubdate: true,
|
||||
@ -110,7 +109,12 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
||||
} else {
|
||||
return HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => setState(() {}),
|
||||
fn: () {
|
||||
setState(() {
|
||||
_futureBuilderFuture =
|
||||
_hotController.queryHotFeed('init');
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -10,8 +10,7 @@ class LiveController extends GetxController {
|
||||
int count = 12;
|
||||
int _currentPage = 1;
|
||||
RxInt crossAxisCount = 2.obs;
|
||||
RxList<LiveItemModel> liveList = [LiveItemModel()].obs;
|
||||
bool isLoadingMore = false;
|
||||
RxList<LiveItemModel> liveList = <LiveItemModel>[].obs;
|
||||
bool flag = false;
|
||||
OverlayEntry? popupDialog;
|
||||
Box setting = GStrorage.setting;
|
||||
@ -39,7 +38,6 @@ class LiveController extends GetxController {
|
||||
}
|
||||
_currentPage += 1;
|
||||
}
|
||||
isLoadingMore = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
||||
import 'package:pilipala/pages/home/index.dart';
|
||||
import 'package:pilipala/pages/main/index.dart';
|
||||
import 'package:pilipala/pages/rcmd/index.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
import 'widgets/live_item.dart';
|
||||
@ -45,8 +44,8 @@ class _LivePageState extends State<LivePage>
|
||||
() {
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 200) {
|
||||
EasyThrottle.throttle('liveList', const Duration(seconds: 1), () {
|
||||
_liveController.isLoadingMore = true;
|
||||
EasyThrottle.throttle('liveList', const Duration(milliseconds: 200),
|
||||
() {
|
||||
_liveController.onLoad();
|
||||
});
|
||||
}
|
||||
@ -108,24 +107,20 @@ class _LivePageState extends State<LivePage>
|
||||
} else {
|
||||
return HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => {},
|
||||
fn: () {
|
||||
setState(() {
|
||||
_futureBuilderFuture =
|
||||
_liveController.queryLiveList('init');
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 缓存数据
|
||||
if (_liveController.liveList.length > 1) {
|
||||
return contentGrid(
|
||||
_liveController, _liveController.liveList);
|
||||
}
|
||||
// 骨架屏
|
||||
else {
|
||||
return contentGrid(_liveController, []);
|
||||
}
|
||||
return contentGrid(_liveController, []);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
LoadingMore(ctr: _liveController)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -44,7 +44,7 @@ class _RcmdPageState extends State<RcmdPage>
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 200) {
|
||||
EasyThrottle.throttle(
|
||||
'my-throttler', const Duration(milliseconds: 500), () {
|
||||
'my-throttler', const Duration(milliseconds: 200), () {
|
||||
_rcmdController.isLoadingMore = true;
|
||||
_rcmdController.onLoad();
|
||||
});
|
||||
@ -113,6 +113,7 @@ class _RcmdPageState extends State<RcmdPage>
|
||||
errMsg: data['msg'],
|
||||
fn: () {
|
||||
setState(() {
|
||||
_rcmdController.isLoadingMore = true;
|
||||
_futureBuilderFuture =
|
||||
_rcmdController.queryRcmdFeed('init');
|
||||
});
|
||||
@ -125,7 +126,6 @@ class _RcmdPageState extends State<RcmdPage>
|
||||
},
|
||||
),
|
||||
),
|
||||
LoadingMore(ctr: _rcmdController),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -188,33 +188,3 @@ class _RcmdPageState extends State<RcmdPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoadingMore extends StatelessWidget {
|
||||
final dynamic ctr;
|
||||
const LoadingMore({super.key, this.ctr});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).padding.bottom + 80,
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (ctr != null) {
|
||||
ctr!.isLoadingMore = true;
|
||||
ctr!.onLoad();
|
||||
}
|
||||
},
|
||||
child: Center(
|
||||
child: Text(
|
||||
'点击加载更多 👇',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline, fontSize: 13),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -187,9 +187,13 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => setState(() {}),
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => setState(() {}),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -105,7 +105,11 @@ class _SearchPanelState extends State<SearchPanel>
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => setState(() {}),
|
||||
fn: () {
|
||||
setState(() {
|
||||
_searchPanelController.onSearch();
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -116,7 +120,11 @@ class _SearchPanelState extends State<SearchPanel>
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: '没有相关数据',
|
||||
fn: () => setState(() {}),
|
||||
fn: () {
|
||||
setState(() {
|
||||
_searchPanelController.onSearch();
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -35,7 +35,7 @@ class SearchVideoPanel extends StatelessWidget {
|
||||
padding: index == 0
|
||||
? const EdgeInsets.only(top: 2)
|
||||
: EdgeInsets.zero,
|
||||
child: VideoCardH(videoItem: i),
|
||||
child: VideoCardH(videoItem: i, showPubdate: true),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -70,7 +70,7 @@ class SearchVideoPanel extends StatelessWidget {
|
||||
controller.selectedType.value = i['type'];
|
||||
ctr!.order.value =
|
||||
i['type'].toString().split('.').last;
|
||||
SmartDialog.showLoading(msg: 'loooad');
|
||||
SmartDialog.showLoading(msg: 'loading');
|
||||
await ctr!.onRefresh();
|
||||
SmartDialog.dismiss();
|
||||
},
|
||||
@ -202,7 +202,7 @@ class VideoPanelController extends GetxController {
|
||||
Get.find<SearchPanelController>(
|
||||
tag: 'video${searchPanelCtr.keyword!}');
|
||||
ctr.duration.value = i['value'];
|
||||
SmartDialog.showLoading(msg: 'loooad');
|
||||
SmartDialog.showLoading(msg: 'loading');
|
||||
await ctr.onRefresh();
|
||||
SmartDialog.dismiss();
|
||||
},
|
||||
|
@ -18,6 +18,7 @@ import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../related/index.dart';
|
||||
import 'widgets/group_panel.dart';
|
||||
|
||||
class VideoIntroController extends GetxController {
|
||||
@ -478,11 +479,15 @@ class VideoIntroController extends GetxController {
|
||||
// 重新获取视频资源
|
||||
final VideoDetailController videoDetailCtr =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
final ReleatedController releatedCtr =
|
||||
Get.find<ReleatedController>(tag: heroTag);
|
||||
videoDetailCtr.bvid = bvid;
|
||||
videoDetailCtr.oid.value = aid;
|
||||
videoDetailCtr.cid.value = cid;
|
||||
videoDetailCtr.danmakuCid.value = cid;
|
||||
videoDetailCtr.queryVideoUrl();
|
||||
releatedCtr.bvid = bvid;
|
||||
releatedCtr.queryRelatedVideo();
|
||||
// 重新请求评论
|
||||
try {
|
||||
/// 未渲染回复组件时可能异常
|
||||
|
@ -1,14 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/video.dart';
|
||||
import '../../../../models/model_hot_video_item.dart';
|
||||
|
||||
class ReleatedController extends GetxController {
|
||||
// 视频aid
|
||||
String bvid = Get.parameters['bvid'] ?? "";
|
||||
// 推荐视频列表
|
||||
List relatedVideoList = [];
|
||||
RxList relatedVideoList = <HotVideoItemModel>[].obs;
|
||||
|
||||
OverlayEntry? popupDialog;
|
||||
|
||||
Future<dynamic> queryRelatedVideo() => VideoHttp.relatedVideoList(bvid: bvid);
|
||||
Future<dynamic> queryRelatedVideo() async {
|
||||
return VideoHttp.relatedVideoList(bvid: bvid).then((value) {
|
||||
if (value['status']) {
|
||||
relatedVideoList.value = value['data'];
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,48 +7,73 @@ import 'package:pilipala/common/widgets/overlay_pop.dart';
|
||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||
import './controller.dart';
|
||||
|
||||
class RelatedVideoPanel extends StatelessWidget {
|
||||
final ReleatedController _releatedController =
|
||||
Get.put(ReleatedController(), tag: Get.arguments?['heroTag']);
|
||||
RelatedVideoPanel({super.key});
|
||||
class RelatedVideoPanel extends StatefulWidget {
|
||||
const RelatedVideoPanel({super.key});
|
||||
|
||||
@override
|
||||
State<RelatedVideoPanel> createState() => _RelatedVideoPanelState();
|
||||
}
|
||||
|
||||
class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late ReleatedController _releatedController;
|
||||
late Future _futureBuilder;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_releatedController =
|
||||
Get.put(ReleatedController(), tag: Get.arguments?['heroTag']);
|
||||
_futureBuilder = _releatedController.queryRelatedVideo();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return FutureBuilder(
|
||||
future: _releatedController.queryRelatedVideo(),
|
||||
future: _futureBuilder,
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SliverToBoxAdapter(child: SizedBox());
|
||||
}
|
||||
if (snapshot.data!['status']) {
|
||||
if (snapshot.data!['status'] && snapshot.data != null) {
|
||||
RxList relatedVideoList = _releatedController.relatedVideoList;
|
||||
// 请求成功
|
||||
return SliverList(
|
||||
return Obx(
|
||||
() => SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
if (index == snapshot.data['data'].length) {
|
||||
return SizedBox(height: MediaQuery.of(context).padding.bottom);
|
||||
} else {
|
||||
return Material(
|
||||
child: VideoCardH(
|
||||
videoItem: snapshot.data['data'][index],
|
||||
showPubdate: true,
|
||||
longPress: () {
|
||||
try {
|
||||
_releatedController.popupDialog =
|
||||
_createPopupDialog(snapshot.data['data'][index]);
|
||||
Overlay.of(context)
|
||||
.insert(_releatedController.popupDialog!);
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
longPressEnd: () {
|
||||
_releatedController.popupDialog?.remove();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}, childCount: snapshot.data['data'].length + 1));
|
||||
if (index == relatedVideoList.length) {
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).padding.bottom);
|
||||
} else {
|
||||
return Material(
|
||||
child: VideoCardH(
|
||||
videoItem: relatedVideoList[index],
|
||||
showPubdate: true,
|
||||
longPress: () {
|
||||
try {
|
||||
_releatedController.popupDialog =
|
||||
_createPopupDialog(_releatedController
|
||||
.relatedVideoList[index]);
|
||||
Overlay.of(context)
|
||||
.insert(_releatedController.popupDialog!);
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
longPressEnd: () {
|
||||
_releatedController.popupDialog?.remove();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}, childCount: relatedVideoList.length + 1),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 请求错误
|
||||
return HttpError(errMsg: '出错了', fn: () {});
|
||||
|
@ -28,7 +28,7 @@ class Utils {
|
||||
}
|
||||
|
||||
static String numFormat(dynamic number) {
|
||||
if (number == null){
|
||||
if (number == null) {
|
||||
return '0';
|
||||
}
|
||||
if (number is String) {
|
||||
@ -63,6 +63,26 @@ class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
// 完全相对时间显示
|
||||
static String formatTimestampToRelativeTime(timeStamp) {
|
||||
var difference = DateTime.now()
|
||||
.difference(DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000));
|
||||
|
||||
if (difference.inDays > 365) {
|
||||
return '${difference.inDays ~/ 365}年前';
|
||||
} else if (difference.inDays > 30) {
|
||||
return '${difference.inDays ~/ 30}个月前';
|
||||
} else if (difference.inDays > 0) {
|
||||
return '${difference.inDays}天前';
|
||||
} else if (difference.inHours > 0) {
|
||||
return '${difference.inHours}小时前';
|
||||
} else if (difference.inMinutes > 0) {
|
||||
return '${difference.inMinutes}分钟前';
|
||||
} else {
|
||||
return '刚刚';
|
||||
}
|
||||
}
|
||||
|
||||
// 时间显示,刚刚,x分钟前
|
||||
static String dateFormat(timeStamp, {formatType = 'list'}) {
|
||||
// 当前时间
|
||||
|
Reference in New Issue
Block a user