diff --git a/assets/images/logo/logo_big.png b/assets/images/logo/logo_big.png new file mode 100644 index 00000000..62370832 Binary files /dev/null and b/assets/images/logo/logo_big.png differ diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index fd3c289c..1217e700 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -1,13 +1,13 @@ import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; -import 'package:pilipala/common/widgets/stat/up.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; // 视频卡片 - 水平布局 class VideoCardH extends StatelessWidget { + // ignore: prefer_typing_uninitialized_variables var videoItem; VideoCardH({Key? key, required this.videoItem}) : super(key: key); @@ -138,13 +138,6 @@ class VideoContent extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - // Image.asset( - // 'assets/images/up_gray.png', - // width: 14, - // height: 12, - // ), - const UpTag(), - const SizedBox(width: 2), Text( videoItem.owner.name, style: TextStyle( diff --git a/lib/http/api.dart b/lib/http/api.dart index b056b7cd..94efb69a 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -55,7 +55,7 @@ class Api { // csrf str CSRF Token(位于cookie) Cookie方式必要 // https://api.bilibili.com/medialist/gateway/coll/resource/deal // https://api.bilibili.com/x/v3/fav/resource/deal - static const String favVideo = '/medialist/gateway/coll/resource/deal'; + static const String favVideo = '/x/v3/fav/resource/deal'; // 判断视频是否被收藏(双端)GET /// aid @@ -68,6 +68,20 @@ class Api { // bvid str 稿件bvid 必要(可选) avid与bvid任选一个 // csrf str CSRF Token(位于cookie) 必要 + // 一键三连 + // https://api.bilibili.com/x/web-interface/archive/like/triple + // aid num 稿件avid 必要(可选) avid与bvid任选一个 + // bvid str 稿件bvid 必要(可选) avid与bvid任选一个 + // csrf str CSRF Token(位于cookie) 必要 + static const String oneThree = '/x/web-interface/archive/like/triple'; + + // 获取指定用户创建的所有收藏夹信息 + // 该接口也能查询目标内容id存在于那些收藏夹中 + // up_mid num 目标用户mid 必要 + // type num 目标内容属性 非必要 默认为全部 0:全部 2:视频稿件 + // rid num 目标 视频稿件avid + static const String videoInFolder = '/x/v3/fav/folder/created/list-all'; + // 视频详情页 相关视频 static const String relatedList = '/x/web-interface/archive/related'; diff --git a/lib/http/init.dart b/lib/http/init.dart index eca0a6d4..240251dd 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -138,13 +138,14 @@ class Request { /* * post请求 */ - post(url, {data, options, cancelToken, extra}) async { + post(url, {data, queryParameters, options, cancelToken, extra}) async { print('post-data: $data'); Response response; try { response = await dio.post( url, data: data, + queryParameters: queryParameters, options: options, cancelToken: cancelToken, ); diff --git a/lib/http/video.dart b/lib/http/video.dart index 002bff22..d01f6889 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -2,6 +2,7 @@ import 'package:pilipala/http/api.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/models/model_hot_video_item.dart'; import 'package:pilipala/models/model_rec_video_item.dart'; +import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/video_detail_res.dart'; /// res.data['code'] == 0 请求正常返回结果 @@ -122,11 +123,26 @@ class VideoHttp { } // 一键三连 + static Future oneThree({required String aid}) async { + var res = await Request().post( + Api.oneThree, + queryParameters: { + 'aid': aid, + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return {'status': true, 'data': res.data['data']}; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } + // (取消)点赞 static Future likeVideo({required String aid, required bool type}) async { var res = await Request().post( Api.likeVideo, - data: { + queryParameters: { 'aid': aid, 'like': type ? 1 : 2, 'csrf': await Request.getCsrf(), @@ -141,20 +157,33 @@ class VideoHttp { // (取消)收藏 static Future favVideo( - {required String aid, required bool type, required String ids}) async { - Map data = {'rid': aid, 'type': 2}; - // type true 添加收藏 false 取消收藏 - if (type) { - data['add_media_ids'] = ids; - } else { - data['del_media_ids'] = ids; - } - var res = await Request() - .post(Api.favVideo, data: {'aid': aid, 'like': type ? 1 : 2}); + {required String aid, + required bool type, + required String addIds, + required String delIds}) async { + var res = await Request().post(Api.favVideo, queryParameters: { + 'rid': aid, + 'type': 2, + 'add_media_ids': addIds, + 'del_media_ids': delIds, + 'csrf': await Request.getCsrf(), + }); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; } else { return {'status': false, 'data': []}; } } + + // 查看视频被收藏在哪个文件夹 + static Future videoInFolder({required int mid, required String rid}) async { + var res = await Request() + .get(Api.videoInFolder, data: {'up_mid': mid, 'rid': rid}); + if (res.data['code'] == 0) { + FavFolderData data = FavFolderData.fromJson(res.data['data']); + return {'status': true, 'data': data}; + } else { + return {'status': false, 'data': []}; + } + } } diff --git a/lib/models/user/fav_folder.dart b/lib/models/user/fav_folder.dart index 0e0e61eb..6d3f9975 100644 --- a/lib/models/user/fav_folder.dart +++ b/lib/models/user/fav_folder.dart @@ -72,7 +72,7 @@ class FavFolderItemData { attr = json['attr']; title = json['title']; cover = json['cover']; - upper = Upper.fromJson(json['upper']); + upper = json['upper'] != null ? Upper.fromJson(json['upper']) : Upper(); coverType = json['cover_type']; intro = json['intro']; ctime = json['ctime']; diff --git a/lib/pages/favDetail/view.dart b/lib/pages/favDetail/view.dart index 1fb48886..e637c6d5 100644 --- a/lib/pages/favDetail/view.dart +++ b/lib/pages/favDetail/view.dart @@ -167,18 +167,27 @@ class _FavDetailPageState extends State { if (snapshot.connectionState == ConnectionState.done) { Map data = snapshot.data; if (data['status']) { - return Obx( - () => SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return FavVideoCardH( - videoItem: _favDetailController - .favDetailData.value.medias![index], - ); - }, - childCount: _favDetailController - .favDetailData.value.medias!.length), - ), - ); + if (_favDetailController.item!.mediaCount == 0) { + return const SliverToBoxAdapter( + child: SizedBox( + height: 300, + child: Center(child: Text('没有内容')), + ), + ); + } else { + return Obx( + () => SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return FavVideoCardH( + videoItem: _favDetailController + .favDetailData.value.medias![index], + ); + }, + childCount: _favDetailController + .favDetailData.value.medias!.length), + ), + ); + } } else { return HttpError( errMsg: data['msg'], @@ -187,8 +196,9 @@ class _FavDetailPageState extends State { } } else { return const SliverToBoxAdapter( - child: Center( - child: Text('加载中'), + child: SizedBox( + height: 300, + child: Center(child: Text('加载中')), ), ); } diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index c5606c79..7182b134 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -25,7 +25,6 @@ class HomeController extends GetxController { freshIdx: _currentPage, ); if (res['status']) { - print('type: $type'); if (type == 'init') { videoList.value = res['data']; } else if (type == 'onRefresh') { diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 6557000c..a18b1e3d 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -122,43 +122,56 @@ class _MediaPageState extends State SizedBox( width: double.infinity, height: 170, - child: ListView( - scrollDirection: Axis.horizontal, - children: [ - const SizedBox(width: 20), - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data; - if (data['status']) { - return Obx(() => Row( - children: [ - if (_mediaController.favFolderData.value.list != - null) ...[ - for (FavFolderItemData i in _mediaController - .favFolderData.value.list!) ...[ - FavFolderItem(item: i), - const SizedBox(width: 14) - ] - ] - ], - )); - } else { - return SizedBox( - height: 160, - child: Center(child: Text(data['msg'])), - ); - } - } else { - // 骨架屏 - return SizedBox(); - } - }), - // for (var i in [1, 2, 3]) ...[const FavFolderItem()], - const SizedBox(width: 10) - ], - ), + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data; + if (data['status']) { + List favFolderList = + _mediaController.favFolderData.value.list!; + int favFolderCount = + _mediaController.favFolderData.value.count!; + bool flag = favFolderCount > favFolderList.length; + return Obx(() => ListView.builder( + itemCount: _mediaController + .favFolderData.value.list!.length + + (flag ? 1 : 0), + itemBuilder: (context, index) { + if (flag && index == favFolderList.length) { + return Padding( + padding: const EdgeInsets.only( + right: 14, bottom: 35), + child: Center( + child: IconButton( + onPressed: () => Get.toNamed('/fav'), + icon: Icon( + Icons.arrow_forward_ios, + size: 18, + color: Theme.of(context).primaryColor, + ), + ), + )); + } else { + return FavFolderItem( + item: _mediaController + .favFolderData.value.list![index], + index: index); + } + }, + scrollDirection: Axis.horizontal, + )); + } else { + return SizedBox( + height: 160, + child: Center(child: Text(data['msg'])), + ); + } + } else { + // 骨架屏 + return SizedBox(); + } + }), ), ], ); @@ -166,59 +179,63 @@ class _MediaPageState extends State } class FavFolderItem extends StatelessWidget { - FavFolderItem({super.key, this.item}); + FavFolderItem({super.key, this.item, this.index}); FavFolderItemData? item; + int? index; @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { - 'mediaId': item!.id.toString(), - }), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 12), - Container( - width: 180, - height: 110, - margin: const EdgeInsets.only(bottom: 8), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Theme.of(context).colorScheme.onInverseSurface, - boxShadow: [ - BoxShadow( - color: Theme.of(context).colorScheme.onInverseSurface, - offset: const Offset(4, -12), // 阴影与容器的距离 - blurRadius: 0.0, // 高斯的标准偏差与盒子的形状卷积。 - spreadRadius: 0.0, // 在应用模糊之前,框应该膨胀的量。 - ), - ], + return Container( + margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14), + child: GestureDetector( + onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { + 'mediaId': item!.id.toString(), + }), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 12), + Container( + width: 180, + height: 110, + margin: const EdgeInsets.only(bottom: 8), + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Theme.of(context).colorScheme.onInverseSurface, + boxShadow: [ + BoxShadow( + color: Theme.of(context).colorScheme.onInverseSurface, + offset: const Offset(4, -12), // 阴影与容器的距离 + blurRadius: 0.0, // 高斯的标准偏差与盒子的形状卷积。 + spreadRadius: 0.0, // 在应用模糊之前,框应该膨胀的量。 + ), + ], + ), + child: LayoutBuilder( + builder: (context, BoxConstraints box) { + return NetworkImgLayer( + src: item!.cover, + width: box.maxWidth, + height: box.maxHeight, + ); + }, + ), ), - child: LayoutBuilder( - builder: (context, BoxConstraints box) { - return NetworkImgLayer( - src: item!.cover, - width: box.maxWidth, - height: box.maxHeight, - ); - }, + Text( + ' ${item!.title}', + overflow: TextOverflow.fade, + maxLines: 1, ), - ), - Text( - ' ${item!.title}', - overflow: TextOverflow.fade, - maxLines: 1, - ), - Text( - ' 共${item!.mediaCount}条视频', - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith(color: Theme.of(context).colorScheme.outline), - ) - ], + Text( + ' 共${item!.mediaCount}条视频', + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith(color: Theme.of(context).colorScheme.outline), + ) + ], + ), ), ); } diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 93b0d1d0..db73fd6f 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -27,6 +27,9 @@ class MineController extends GetxController { } Future queryUserInfo() async { + if (user.get(UserBoxKey.userLogin) == null) { + return {'status': false}; + } var res = await UserHttp.userInfo(); if (res['status']) { if (res['data'].isLogin) { diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 85e43ad0..e0a72d96 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -64,6 +64,7 @@ class _MinePageState extends State { future: _mineController.queryUserInfo(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { + print(snapshot.data); if (snapshot.data['status']) { return Obx(() => userInfoBuild()); } else { diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index c7e7b831..71616423 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/video.dart'; +import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; +import 'package:pilipala/utils/storage.dart'; class VideoIntroController extends GetxController { // 视频aid @@ -34,6 +37,11 @@ class VideoIntroController extends GetxController { RxBool hasCoin = false.obs; // 是否收藏 RxBool hasFav = false.obs; + Box user = GStrorage.user; + bool userLogin = false; + Rx favFolderData = FavFolderData().obs; + List addMediaIdsNew = []; + List delMediaIdsNew = []; @override void onInit() { @@ -51,6 +59,7 @@ class VideoIntroController extends GetxController { videoItem!['owner'] = args.owner; } } + userLogin = user.get(UserBoxKey.userLogin) != null; } // 获取视频简介 @@ -66,12 +75,14 @@ class VideoIntroController extends GetxController { } // 获取到粉丝数再返回 await queryUserStat(); - // 获取点赞状态 - queryHasLikeVideo(); - // 获取投币状态 - queryHasCoinVideo(); - // 获取收藏状态 - queryHasFavVideo(); + if (userLogin) { + // 获取点赞状态 + queryHasLikeVideo(); + // 获取投币状态 + queryHasCoinVideo(); + // 获取收藏状态 + queryHasFavVideo(); + } return result; } @@ -104,12 +115,54 @@ class VideoIntroController extends GetxController { } // 一键三连 + Future actionOneThree() async { + if (hasLike.value && hasCoin.value && hasFav.value) { + // 已点赞、投币、收藏 + SmartDialog.showToast('🙏 UP已经收到了~'); + return false; + } + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('一键三连 给UP送温暖'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: const Text('点错了')), + TextButton( + onPressed: () async { + var result = await VideoHttp.oneThree(aid: aid); + if (result['status']) { + hasLike.value = result["data"]["like"]; + hasCoin.value = result["data"]["coin"]; + hasFav.value = result["data"]["fav"]; + SmartDialog.showToast('三连成功 🎉'); + } else { + SmartDialog.showToast(result['msg']); + } + SmartDialog.dismiss(); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } // (取消)点赞 Future actionLikeVideo() async { var result = await VideoHttp.likeVideo(aid: aid, type: !hasLike.value); if (result['status']) { hasLike.value = result["data"] == 1 ? true : false; + if (hasLike.value) { + SmartDialog.showToast('已点赞 👍'); + } else { + SmartDialog.showToast('取消赞'); + } } else { SmartDialog.showToast(result['msg']); } @@ -122,12 +175,58 @@ class VideoIntroController extends GetxController { // (取消)收藏 Future actionFavVideo() async { - print('(取消)收藏'); - // var result = await VideoHttp.favVideo(aid: aid, type: true, ids: ''); + try { + for (var i in favFolderData.value.list!.toList()) { + if (i.favState == 1) { + addMediaIdsNew.add(i.id); + } else { + delMediaIdsNew.add(i.id); + } + } + } catch (e) {} + var result = await VideoHttp.favVideo( + aid: aid, + type: true, + addIds: addMediaIdsNew.join(','), + delIds: delMediaIdsNew.join(',')); + if (result['status']) { + if (result['data']['prompt']) { + addMediaIdsNew = []; + delMediaIdsNew = []; + Get.back(); + // 重新获取收藏状态 + queryHasFavVideo(); + SmartDialog.showToast('✅ 操作成功'); + } + } } // 分享视频 Future actionShareVideo() async { print('分享视频'); } + + Future queryVideoInFolder() async { + var result = await VideoHttp.videoInFolder( + mid: user.get(UserBoxKey.userMid), rid: aid); + if (result['status']) { + favFolderData.value = result['data']; + } + return result; + } + + // 选择文件夹 + onChoose(bool checkValue, int index) { + List datalist = favFolderData.value.list!; + for (var i = 0; i < datalist.length; i++) { + if (i == index) { + datalist[i].favState = checkValue == true ? 1 : 0; + datalist[i].mediaCount = checkValue == true + ? datalist[i].mediaCount! + 1 + : datalist[i].mediaCount! - 1; + } + } + favFolderData.value.list = datalist; + favFolderData.refresh(); + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index f9027cee..843126ed 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -54,10 +54,7 @@ class _VideoIntroPanelState extends State if (snapshot.data['status']) { // 请求成功 // return _buildView(context, false, videoDetail); - return VideoInfo( - loadingStatus: false, - videoDetail: videoDetail, - videoIntroController: videoIntroController); + return VideoInfo(loadingStatus: false, videoDetail: videoDetail); } else { // 请求错误 return HttpError( @@ -66,10 +63,7 @@ class _VideoIntroPanelState extends State ); } } else { - return VideoInfo( - loadingStatus: true, - videoDetail: videoDetail, - videoIntroController: videoIntroController); + return VideoInfo(loadingStatus: true, videoDetail: videoDetail); } }, ); @@ -79,13 +73,8 @@ class _VideoIntroPanelState extends State class VideoInfo extends StatefulWidget { bool loadingStatus = false; VideoDetailData? videoDetail; - VideoIntroController? videoIntroController; - VideoInfo( - {Key? key, - required this.loadingStatus, - this.videoDetail, - this.videoIntroController}) + VideoInfo({Key? key, required this.loadingStatus, this.videoDetail}) : super(key: key); @override @@ -94,6 +83,8 @@ class VideoInfo extends StatefulWidget { class _VideoInfoState extends State with TickerProviderStateMixin { Map videoItem = Get.put(VideoIntroController()).videoItem!; + final VideoIntroController videoIntroController = + Get.put(VideoIntroController(), tag: Get.arguments['heroTag']); bool isExpand = false; /// 手动控制动画的控制器 @@ -137,7 +128,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), actions: [ TextButton( - onPressed: () => Get.back(), + onPressed: () => videoIntroController.actionFavVideo(), child: const Text('完成'), ), const SizedBox(width: 6), @@ -146,30 +137,34 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Expanded( child: Material( child: FutureBuilder( - future: _favController.queryFavFolder(), + future: videoIntroController.queryVideoInFolder(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { Map data = snapshot.data as Map; if (data['status']) { return Obx( () => ListView.builder( - itemCount: _favController + itemCount: videoIntroController .favFolderData.value.list!.length + 1, itemBuilder: (context, index) { if (index == 0) { - return const SizedBox(height: 15); + return const SizedBox(height: 10); } else { return ListTile( - onTap: () {}, + onTap: () => videoIntroController.onChoose( + videoIntroController.favFolderData.value + .list![index - 1].favState != + 1, + index - 1), dense: true, leading: const Icon(Icons.folder_special_outlined), minLeadingWidth: 0, - title: Text(_favController.favFolderData.value - .list![index - 1].title!), + title: Text(videoIntroController.favFolderData + .value.list![index - 1].title!), subtitle: Text( - '${_favController.favFolderData.value.list![index - 1].mediaCount}个内容', + '${videoIntroController.favFolderData.value.list![index - 1].mediaCount}个内容', style: TextStyle( color: Theme.of(context) .colorScheme @@ -182,8 +177,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { trailing: Transform.scale( scale: 0.9, child: Checkbox( - value: false, - onChanged: (bool? checkValue) {}, + value: videoIntroController + .favFolderData + .value + .list![index - 1] + .favState == + 1, + onChanged: (bool? checkValue) => + videoIntroController.onChoose( + checkValue!, index - 1), ), ), ); @@ -302,65 +304,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ], ), ), - const SizedBox(height: 12), - Row( - children: [ - NetworkImgLayer( - type: 'avatar', - src: !widget.loadingStatus - ? widget.videoDetail!.owner!.face - : videoItem['owner'].face, - width: 38, - height: 38, - fadeInDuration: Duration.zero, - fadeOutDuration: Duration.zero, - ), - const SizedBox(width: 14), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(!widget.loadingStatus - ? widget.videoDetail!.owner!.name - : videoItem['owner'].name), - // const SizedBox(width: 10), - Text( - widget.loadingStatus - ? '- 粉丝' - : '${Utils.numFormat(widget.videoIntroController!.userStat['follower'])}粉丝', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize, - color: Theme.of(context).colorScheme.outline), - ), - ], - ), - const Spacer(), - AnimatedOpacity( - opacity: widget.loadingStatus ? 0 : 1, - duration: const Duration(milliseconds: 150), - child: SizedBox( - height: 36, - child: ElevatedButton( - onPressed: () {}, - child: Row( - children: const [ - Icon( - CupertinoIcons.plus, - size: 16, - ), - SizedBox(width: 4), - Text('关注'), - ], - ), - ), - ), - ), - const SizedBox(width: 4), - ], - ), - const SizedBox(height: 10), // 简介 默认收起 if (!widget.loadingStatus) ExpandedSection( @@ -392,8 +335,64 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ), - const SizedBox(height: 5), - _actionGrid(context, widget.videoIntroController), + const SizedBox(height: 8), + _actionGrid(context, videoIntroController), + Divider( + height: 26, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + Row( + children: [ + NetworkImgLayer( + type: 'avatar', + src: !widget.loadingStatus + ? widget.videoDetail!.owner!.face + : videoItem['owner'].face, + width: 38, + height: 38, + fadeInDuration: Duration.zero, + fadeOutDuration: Duration.zero, + ), + const SizedBox(width: 14), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(!widget.loadingStatus + ? widget.videoDetail!.owner!.name + : videoItem['owner'].name), + // const SizedBox(width: 10), + Text( + widget.loadingStatus + ? '- 粉丝' + : '${Utils.numFormat(videoIntroController.userStat['follower'])}粉丝', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize, + color: Theme.of(context).colorScheme.outline), + ), + ], + ), + const Spacer(), + AnimatedOpacity( + opacity: widget.loadingStatus ? 0 : 1, + duration: const Duration(milliseconds: 150), + child: SizedBox( + height: 36, + child: ElevatedButton( + onPressed: () {}, + child: const Text('关注'), + ), + ), + ), + ], + ), + Divider( + height: 26, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + // const SizedBox(height: 10), ], ) : const Center(child: CircularProgressIndicator()), @@ -412,6 +411,29 @@ class _VideoInfoState extends State with TickerProviderStateMixin { crossAxisCount: 5, childAspectRatio: 1.25, children: [ + // ActionItem( + // icon: const Icon(FontAwesomeIcons.s), + // selectIcon: const Icon(FontAwesomeIcons.s), + // onTap: () => {}, + // selectStatus: true, + // loadingStatus: false, + // text: '三连', + // ), + // Column( + // children: [], + // ), + InkWell( + onTap: () => videoIntroController.actionOneThree(), + borderRadius: StyleString.mdRadius, + child: Padding( + padding: const EdgeInsets.all(12), + child: Image.asset( + 'assets/images/logo/logo_big.png', + width: 10, + height: 10, + ), + ), + ), Obx( () => ActionItem( icon: const Icon(FontAwesomeIcons.thumbsUp), @@ -423,13 +445,13 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? widget.videoDetail!.stat!.like!.toString() : '-'), ), - ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsDown), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), - onTap: () => {}, - selectStatus: false, - loadingStatus: widget.loadingStatus, - text: '不喜欢'), + // ActionItem( + // icon: const Icon(FontAwesomeIcons.thumbsDown), + // selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), + // onTap: () => {}, + // selectStatus: false, + // loadingStatus: widget.loadingStatus, + // text: '不喜欢'), Obx( () => ActionItem( icon: const Icon(FontAwesomeIcons.b), @@ -445,7 +467,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { () => ActionItem( icon: const Icon(FontAwesomeIcons.star), selectIcon: const Icon(FontAwesomeIcons.star), - // onTap: () => videoIntroController.actionFavVideo(), onTap: () => showFavBottomSheet(), selectStatus: videoIntroController.hasFav.value, loadingStatus: widget.loadingStatus, @@ -488,37 +509,32 @@ class ActionItem extends StatelessWidget { @override Widget build(BuildContext context) { - return Material( - child: Ink( - child: InkWell( - onTap: () => onTap!(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - selectStatus - ? Icon(selectIcon!.icon!, - size: 21, color: Theme.of(context).primaryColor) - : Icon(icon!.icon!, - size: 21, color: Theme.of(context).colorScheme.outline), - const SizedBox(height: 4), - AnimatedOpacity( - opacity: loadingStatus! ? 0 : 1, - duration: const Duration(milliseconds: 200), - child: Text( - text!, - style: TextStyle( - color: selectStatus - ? Theme.of(context).primaryColor - : Theme.of(context).colorScheme.outline, - fontSize: - Theme.of(context).textTheme.labelSmall?.fontSize), - ), - ), - ], + return InkWell( + onTap: () => onTap!(), + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + selectStatus + ? Icon(selectIcon!.icon!, + size: 21, color: Theme.of(context).primaryColor) + : Icon(icon!.icon!, + size: 21, color: Theme.of(context).colorScheme.outline), + const SizedBox(height: 4), + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: Text( + text ?? '', + style: TextStyle( + color: selectStatus + ? Theme.of(context).primaryColor + : Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall?.fontSize), + ), ), - ), + ], ), ); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 030ccc8e..3265ea24 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -114,23 +114,11 @@ class _VideoDetailPageState extends State { children: [ Builder( builder: (context) { - return CustomScrollView( - key: const PageStorageKey('简介'), + return const CustomScrollView( + key: PageStorageKey('简介'), slivers: [ - const VideoIntroPanel(), - SliverPadding( - padding: - const EdgeInsets.only(top: 8, bottom: 5), - sliver: SliverToBoxAdapter( - child: Divider( - height: 1, - color: Theme.of(context) - .dividerColor - .withOpacity(0.1), - ), - ), - ), - const RelatedVideoPanel(), + VideoIntroPanel(), + RelatedVideoPanel(), ], ); }, diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index eee80c02..9fe1b670 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -1,10 +1,13 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/user.dart'; +import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/mine/index.dart'; import 'package:pilipala/utils/cookie.dart'; +import 'package:pilipala/utils/storage.dart'; import 'package:webview_cookie_manager/webview_cookie_manager.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -52,10 +55,15 @@ class WebviewController extends GetxController { await WebviewCookieManager().getCookies(HttpString.baseUrl); await SetCookie.onSet(cookies, HttpString.baseUrl); await SetCookie.onSet(apiCookies, HttpString.baseApiUrl); + await UserHttp.userInfo(); var result = await UserHttp.userInfo(); + print('网页登录: $result'); if (result['status'] && result['data'].isLogin) { SmartDialog.showToast('登录成功'); - Get.find().userInfo = result['data']; + Box user = GStrorage.user; + user.put(UserBoxKey.userLogin, true); + Get.find().userInfo.value = result['data']; + Get.find().queryRcmdFeed('onRefresh'); Get.back(); } } catch (e) { diff --git a/pubspec.yaml b/pubspec.yaml index a856f809..47c6ee49 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -113,6 +113,7 @@ flutter: assets: - assets/images/ - assets/images/lv/ + - assets/images/logo/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware