Merge branch 'main' into design

This commit is contained in:
guozhigq
2024-05-28 22:45:02 +08:00
11 changed files with 112 additions and 145 deletions

View File

@ -244,7 +244,7 @@ class HistoryItem extends StatelessWidget {
), ),
), ),
), ),
videoItem.progress != 0 videoItem.progress != 0 && videoItem.duration != 0
? Positioned( ? Positioned(
left: 3, left: 3,
right: 3, right: 3,

View File

@ -10,9 +10,8 @@ class HistorySearchController extends GetxController {
final FocusNode searchFocusNode = FocusNode(); final FocusNode searchFocusNode = FocusNode();
RxString searchKeyWord = ''.obs; RxString searchKeyWord = ''.obs;
String hintText = '搜索'; String hintText = '搜索';
RxString loadingStatus = 'init'.obs; RxBool loadingStatus = false.obs;
RxString loadingText = '加载中...'.obs; RxString loadingText = '加载中...'.obs;
bool hasRequest = false;
late int mid; late int mid;
RxString uname = ''.obs; RxString uname = ''.obs;
int pn = 1; int pn = 1;
@ -36,8 +35,7 @@ class HistorySearchController extends GetxController {
// 提交搜索内容 // 提交搜索内容
void submit() { void submit() {
loadingStatus.value = 'loading'; if (!loadingStatus.value) {
if (hasRequest) {
pn = 1; pn = 1;
searchHistories(); searchHistories();
} }
@ -48,6 +46,7 @@ class HistorySearchController extends GetxController {
if (type == 'onLoad' && loadingText.value == '没有更多了') { if (type == 'onLoad' && loadingText.value == '没有更多了') {
return; return;
} }
loadingStatus.value = true;
var res = await UserHttp.searchHistory( var res = await UserHttp.searchHistory(
pn: pn, pn: pn,
keyword: controller.value.text, keyword: controller.value.text,
@ -63,9 +62,8 @@ class HistorySearchController extends GetxController {
loadingText.value = '没有更多了'; loadingText.value = '没有更多了';
} }
pn += 1; pn += 1;
hasRequest = true;
} }
loadingStatus.value = 'finish'; loadingStatus.value = false;
return res; return res;
} }
@ -86,6 +84,6 @@ class HistorySearchController extends GetxController {
historyList.removeWhere((e) => e.kid == kid); historyList.removeWhere((e) => e.kid == kid);
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);
} }
loadingStatus.value = 'finish'; // loadingStatus.value = fasle;
} }
} }

View File

@ -2,7 +2,6 @@ import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/pages/history/widgets/item.dart'; import 'package:pilipala/pages/history/widgets/item.dart';
@ -16,20 +15,19 @@ class HistorySearchPage extends StatefulWidget {
} }
class _HistorySearchPageState extends State<HistorySearchPage> { class _HistorySearchPageState extends State<HistorySearchPage> {
final HistorySearchController _historySearchCtr = final HistorySearchController _hisCtr = Get.put(HistorySearchController());
Get.put(HistorySearchController());
late ScrollController scrollController; late ScrollController scrollController;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
scrollController = _historySearchCtr.scrollController; scrollController = _hisCtr.scrollController;
scrollController.addListener( scrollController.addListener(
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) { scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('history', const Duration(seconds: 1), () { EasyThrottle.throttle('history', const Duration(seconds: 1), () {
_historySearchCtr.onLoad(); _hisCtr.onLoad();
}); });
} }
}, },
@ -50,19 +48,19 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
titleSpacing: 0, titleSpacing: 0,
actions: [ actions: [
IconButton( IconButton(
onPressed: () => _historySearchCtr.submit(), onPressed: () => _hisCtr.submit(),
icon: const Icon(Icons.search_outlined, size: 22)), icon: const Icon(Icons.search_outlined, size: 22)),
const SizedBox(width: 10) const SizedBox(width: 10)
], ],
title: Obx( title: Obx(
() => TextField( () => TextField(
autofocus: true, autofocus: true,
focusNode: _historySearchCtr.searchFocusNode, focusNode: _hisCtr.searchFocusNode,
controller: _historySearchCtr.controller.value, controller: _hisCtr.controller.value,
textInputAction: TextInputAction.search, textInputAction: TextInputAction.search,
onChanged: (value) => _historySearchCtr.onChange(value), onChanged: (value) => _hisCtr.onChange(value),
decoration: InputDecoration( decoration: InputDecoration(
hintText: _historySearchCtr.hintText, hintText: _hisCtr.hintText,
border: InputBorder.none, border: InputBorder.none,
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon( icon: Icon(
@ -70,104 +68,62 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
size: 22, size: 22,
color: Theme.of(context).colorScheme.outline, color: Theme.of(context).colorScheme.outline,
), ),
onPressed: () => _historySearchCtr.onClear(), onPressed: () => _hisCtr.onClear(),
), ),
), ),
onSubmitted: (String value) => _historySearchCtr.submit(), onSubmitted: (String value) => _hisCtr.submit(),
), ),
), ),
), ),
body: Obx( body: Obx(
() => Column( () {
children: _historySearchCtr.loadingStatus.value == 'init' return _hisCtr.loadingStatus.value && _hisCtr.historyList.isEmpty
? [const SizedBox()] ? ListView.builder(
: [ itemCount: 10,
Expanded( itemBuilder: (context, index) {
child: FutureBuilder( return const VideoCardHSkeleton();
future: _historySearchCtr.searchHistories(), },
builder: (context, snapshot) { )
if (snapshot.connectionState == ConnectionState.done) { : _hisCtr.historyList.isNotEmpty
Map data = snapshot.data as Map;
if (data['status']) {
return Obx(
() => _historySearchCtr.historyList.isNotEmpty
? ListView.builder( ? ListView.builder(
controller: scrollController, controller: scrollController,
itemCount: itemCount: _hisCtr.historyList.length + 1,
_historySearchCtr.historyList.length +
1,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == if (index == _hisCtr.historyList.length) {
_historySearchCtr
.historyList.length) {
return Container( return Container(
height: MediaQuery.of(context) height: MediaQuery.of(context).padding.bottom + 60,
.padding
.bottom +
60,
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: MediaQuery.of(context) bottom: MediaQuery.of(context).padding.bottom),
.padding
.bottom),
child: Center( child: Center(
child: Obx( child: Obx(
() => Text( () => Text(
_historySearchCtr _hisCtr.loadingText.value,
.loadingText.value,
style: TextStyle( style: TextStyle(
color: Theme.of(context) color:
.colorScheme Theme.of(context).colorScheme.outline,
.outline, fontSize: 13,
fontSize: 13), ),
), ),
), ),
), ),
); );
} else { } else {
return HistoryItem( return HistoryItem(
videoItem: _historySearchCtr videoItem: _hisCtr.historyList[index],
.historyList[index], ctr: _hisCtr,
ctr: _historySearchCtr,
onChoose: null, onChoose: null,
onUpdateMultiple: () => null, onUpdateMultiple: () => null,
); );
} }
}, },
) )
: _historySearchCtr.loadingStatus.value ==
'loading'
? const SizedBox(child: Text('加载中...'))
: const CustomScrollView( : const CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
NoData(), NoData(),
], ],
),
); );
} else {
return CustomScrollView(
slivers: <Widget>[
HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
)
],
);
}
} else {
// 骨架屏
return ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return const VideoCardHSkeleton();
},
);
}
}, },
), ),
),
],
),
),
); );
} }
} }

View File

@ -1,5 +1,4 @@
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/http/reply.dart'; import 'package:pilipala/http/reply.dart';
@ -15,7 +14,6 @@ class VideoReplyController extends GetxController {
this.rpid, this.rpid,
this.replyLevel, this.replyLevel,
); );
final ScrollController scrollController = ScrollController();
// 视频aid 请求时使用的oid // 视频aid 请求时使用的oid
int? aid; int? aid;
// 层级 2为楼中楼 // 层级 2为楼中楼

View File

@ -67,13 +67,12 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
vsync: this, duration: const Duration(milliseconds: 300)); vsync: this, duration: const Duration(milliseconds: 300));
_futureBuilderFuture = _videoReplyController.queryReplyList(); _futureBuilderFuture = _videoReplyController.queryReplyList();
scrollController = ScrollController();
fabAnimationCtr.forward(); fabAnimationCtr.forward();
scrollListener(); scrollListener();
} }
void scrollListener() { void scrollListener() {
scrollController = _videoReplyController.scrollController;
scrollController.addListener( scrollController.addListener(
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
@ -185,7 +184,8 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
builder: (BuildContext context, snapshot) { builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
var data = snapshot.data; var data = snapshot.data;
if (data['status']) { if (_videoReplyController.replyList.isNotEmpty ||
(data && data['status'])) {
// 请求成功 // 请求成功
return Obx( return Obx(
() => _videoReplyController.isLoadingMore && () => _videoReplyController.isLoadingMore &&

View File

@ -1,3 +1,4 @@
import 'package:appscheme/appscheme.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -11,6 +12,7 @@ import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/preview/index.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart';
import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
@ -643,34 +645,17 @@ InlineSpan buildContent(
'', '',
); );
} else { } else {
final String pathSegment = Uri.parse(matchStr).path; Uri uri = Uri.parse(matchStr);
Map matchRes = IdUtils.matchAvorBv(input: pathSegment); SchemeEntity scheme = SchemeEntity(
List matchKeys = matchRes.keys.toList(); scheme: uri.scheme,
if (matchKeys.isNotEmpty) { host: uri.host,
UrlUtils.matchUrlPush( port: uri.port,
matchRes.containsKey('AV') path: uri.path,
? matchRes['AV']! as int query: uri.queryParameters,
: matchRes['BV'], source: '',
title, dataString: matchStr,
matchStr,
); );
} else { PiliSchame.fullPathPush(scheme);
final String redirectUrl =
await UrlUtils.parseRedirectUrl(matchStr);
// if (redirectUrl == matchStr) {
// Clipboard.setData(ClipboardData(text: matchStr));
// SmartDialog.showToast('地址可能有误');
// return;
// }
Get.toNamed(
'/webview',
parameters: {
'url': redirectUrl,
'type': 'url',
'pageTitle': title
},
);
}
} }
} else { } else {
if (appUrlSchema.startsWith('bilibili://search')) { if (appUrlSchema.startsWith('bilibili://search')) {

View File

@ -935,7 +935,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
begin: 0.0, begin: 0.0,
end: _hideSeekBackwardButton.value ? 0.0 : 1.0, end: _hideSeekBackwardButton.value ? 0.0 : 1.0,
), ),
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 200),
builder: (BuildContext context, double value, builder: (BuildContext context, double value,
Widget? child) => Widget? child) =>
Opacity( Opacity(
@ -978,7 +978,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
begin: 0.0, begin: 0.0,
end: _hideSeekForwardButton.value ? 0.0 : 1.0, end: _hideSeekForwardButton.value ? 0.0 : 1.0,
), ),
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 200),
builder: (BuildContext context, double value, builder: (BuildContext context, double value,
Widget? child) => Widget? child) =>
Opacity( Opacity(

View File

@ -20,6 +20,13 @@ class BackwardSeekIndicatorState extends State<BackwardSeekIndicator> {
Timer? timer; Timer? timer;
@override
void setState(VoidCallback fn) {
if (mounted) {
super.setState(fn);
}
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();

View File

@ -20,6 +20,13 @@ class ForwardSeekIndicatorState extends State<ForwardSeekIndicator> {
Timer? timer; Timer? timer;
@override
void setState(VoidCallback fn) {
if (mounted) {
super.setState(fn);
}
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();

View File

@ -95,7 +95,7 @@ class PiliSchame {
} }
} }
if (scheme == 'https') { if (scheme == 'https') {
_fullPathPush(value); fullPathPush(value);
} }
} }
@ -126,7 +126,7 @@ class PiliSchame {
} }
} }
static Future<void> _fullPathPush(SchemeEntity value) async { static Future<void> fullPathPush(SchemeEntity value) async {
// https://m.bilibili.com/bangumi/play/ss39708 // https://m.bilibili.com/bangumi/play/ss39708
// https | m.bilibili.com | /bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708
// final String scheme = value.scheme!; // final String scheme = value.scheme!;
@ -135,8 +135,6 @@ class PiliSchame {
Map<String, String>? query = value.query; Map<String, String>? query = value.query;
RegExp regExp = RegExp(r'^((www\.)|(m\.))?bilibili\.com$'); RegExp regExp = RegExp(r'^((www\.)|(m\.))?bilibili\.com$');
if (regExp.hasMatch(host)) { if (regExp.hasMatch(host)) {
print('bilibili.com host: $host');
print('bilibili.com path: $path');
final String lastPathSegment = path!.split('/').last; final String lastPathSegment = path!.split('/').last;
if (path.startsWith('/video')) { if (path.startsWith('/video')) {
Map matchRes = IdUtils.matchAvorBv(input: path); Map matchRes = IdUtils.matchAvorBv(input: path);
@ -236,6 +234,24 @@ class PiliSchame {
print('个人空间'); print('个人空间');
Get.toNamed('/member?mid=$area', arguments: {'face': ''}); Get.toNamed('/member?mid=$area', arguments: {'face': ''});
break; break;
default:
final Map<String, dynamic> map =
IdUtils.matchAvorBv(input: area.split('?').first);
if (map.containsKey('AV')) {
_videoPush(map['AV']! as int, null);
} else if (map.containsKey('BV')) {
_videoPush(null, map['BV'] as String);
} else {
Get.toNamed(
'/webview',
parameters: {
'url': value.dataString ?? "",
'type': 'url',
'pageTitle': ''
},
);
}
break;
} }
} }
} }

View File

@ -44,7 +44,7 @@ class UrlUtils {
final String bv = matchRes['BV']; final String bv = matchRes['BV'];
final Map res = await SearchHttp.ab2cWithPic(bvid: bv); final Map res = await SearchHttp.ab2cWithPic(bvid: bv);
final int cid = res['cid']; final int cid = res['cid'];
final String pic = res['pic']; final String? pic = res['pic'];
final String heroTag = Utils.makeHeroTag(bv); final String heroTag = Utils.makeHeroTag(bv);
await Get.toNamed( await Get.toNamed(
'/video?bvid=$bv&cid=$cid', '/video?bvid=$bv&cid=$cid',