diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 06c35974..173db853 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -36,7 +36,7 @@ class NetworkImgLayer extends StatelessWidget { final int defaultImgQuality = GlobalData().imgQuality; final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; - print(imageUrl); + // print(imageUrl); int? memCacheWidth, memCacheHeight; double aspectRatio = (width / height).toDouble(); diff --git a/lib/http/api.dart b/lib/http/api.dart index b6975c4b..1735902c 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -511,4 +511,13 @@ class Api { /// 取消订阅 static const String cancelSub = '/x/v3/fav/season/unfav'; + + /// 动态转发 + static const String dynamicForwardUrl = '/x/dynamic/feed/create/submit_check'; + + /// 创建动态 + static const String dynamicCreate = '/x/dynamic/feed/create/dyn'; + + /// 删除收藏夹 + static const String delFavFolder = '/x/v3/fav/folder/del'; } diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index d62de12f..63dea4ff 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import '../models/dynamics/result.dart'; import '../models/dynamics/up.dart'; import 'index.dart'; @@ -117,4 +118,94 @@ class DynamicsHttp { }; } } + + static Future dynamicForward() async { + var res = await Request().post( + Api.dynamicForwardUrl, + queryParameters: { + 'csrf': await Request.getCsrf(), + 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, + 'x-bili-web-req-json': {'spm_id': '333.999'}, + }, + data: { + 'attach_card': null, + 'scene': 4, + 'content': { + 'conetents': [ + {'raw_text': "2", 'type': 1, 'biz_id': ""} + ] + } + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } + + static Future dynamicCreate({ + required int mid, + required int scene, + int? oid, + String? dynIdStr, + String? rawText, + }) async { + DateTime now = DateTime.now(); + int timestamp = now.millisecondsSinceEpoch ~/ 1000; + Random random = Random(); + int randomNumber = random.nextInt(9000) + 1000; + String uploadId = '${mid}_${timestamp}_$randomNumber'; + + Map webRepostSrc = { + 'dyn_id_str': dynIdStr ?? '', + }; + + /// 投稿转发 + if (scene == 5) { + webRepostSrc = { + 'revs_id': {'dyn_type': 8, 'rid': oid} + }; + } + var res = await Request().post(Api.dynamicCreate, queryParameters: { + 'platform': 'web', + 'csrf': await Request.getCsrf(), + 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, + 'x-bili-web-req-json': {'spm_id': '333.999'}, + }, data: { + 'dyn_req': { + 'content': { + 'contents': [ + {'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''} + ] + }, + 'scene': scene, + 'attach_card': null, + 'upload_id': uploadId, + 'meta': { + 'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'} + } + }, + 'web_repost_src': webRepostSrc + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/http/user.dart b/lib/http/user.dart index fea0a22e..dfdf187e 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -395,4 +395,21 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + // 删除文件夹 + static Future delFavFolder({required int mediaIds}) async { + var res = await Request().post( + Api.delFavFolder, + queryParameters: { + 'media_ids': mediaIds, + 'platform': 'web', + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return {'status': true}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index a620b5ed..c6ec682a 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -183,6 +183,7 @@ class _DynamicDetailPageState extends State scrollController.removeListener(() {}); fabAnimationCtr.dispose(); scrollController.dispose(); + titleStreamC.close(); super.dispose(); } diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 0ca09b8c..51ef3952 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -3,38 +3,58 @@ 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:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:status_bar_control/status_bar_control.dart'; +import 'rich_node_panel.dart'; class ActionPanel extends StatefulWidget { const ActionPanel({ super.key, - this.item, + required this.item, }); // ignore: prefer_typing_uninitialized_variables - final item; + final DynamicItemModel item; @override State createState() => _ActionPanelState(); } -class _ActionPanelState extends State { +class _ActionPanelState extends State + with TickerProviderStateMixin { final DynamicsController _dynamicsController = Get.put(DynamicsController()); late ModuleStatModel stat; bool isProcessing = false; + double defaultHeight = 260; + RxDouble height = 0.0.obs; + RxBool isExpand = false.obs; + late double statusHeight; + TextEditingController _inputController = TextEditingController(); + FocusNode myFocusNode = FocusNode(); + String _inputText = ''; + void Function()? handleState(Future Function() action) { - return isProcessing ? null : () async { - setState(() => isProcessing = true); - await action(); - setState(() => isProcessing = false); - }; + return isProcessing + ? null + : () async { + isProcessing = true; + await action(); + isProcessing = false; + }; } + @override void initState() { super.initState(); - stat = widget.item!.modules.moduleStat; + stat = widget.item.modules!.moduleStat!; + onInit(); + } + + onInit() async { + statusHeight = await StatusBarControl.getHeight; } // 动态点赞 @@ -43,7 +63,7 @@ class _ActionPanelState extends State { var item = widget.item!; String dynamicId = item.idStr!; // 1 已点赞 2 不喜欢 0 未操作 - Like like = item.modules.moduleStat.like; + Like like = item.modules!.moduleStat!.like!; int count = like.count == '点赞' ? 0 : int.parse(like.count ?? '0'); bool status = like.status!; int up = status ? 2 : 1; @@ -51,15 +71,15 @@ class _ActionPanelState extends State { if (res['status']) { SmartDialog.showToast(!status ? '点赞成功' : '取消赞'); if (up == 1) { - item.modules.moduleStat.like.count = (count + 1).toString(); - item.modules.moduleStat.like.status = true; + item.modules!.moduleStat!.like!.count = (count + 1).toString(); + item.modules!.moduleStat!.like!.status = true; } else { if (count == 1) { - item.modules.moduleStat.like.count = '点赞'; + item.modules!.moduleStat!.like!.count = '点赞'; } else { - item.modules.moduleStat.like.count = (count - 1).toString(); + item.modules!.moduleStat!.like!.count = (count - 1).toString(); } - item.modules.moduleStat.like.status = false; + item.modules!.moduleStat!.like!.status = false; } setState(() {}); } else { @@ -67,17 +87,307 @@ class _ActionPanelState extends State { } } + // 转发动态预览 + Widget dynamicPreview() { + ItemModulesModel? modules = widget.item.modules; + final String type = widget.item.type!; + String? cover = modules?.moduleAuthor?.face; + switch (type) { + /// 图文动态 + case 'DYNAMIC_TYPE_DRAW': + cover = modules?.moduleDynamic?.major?.opus?.pics?.first.url; + + /// 投稿 + case 'DYNAMIC_TYPE_AV': + cover = modules?.moduleDynamic?.major?.archive?.cover; + + /// 转发的动态 + case 'DYNAMIC_TYPE_FORWARD': + String forwardType = widget.item.orig!.type!; + switch (forwardType) { + /// 图文动态 + case 'DYNAMIC_TYPE_DRAW': + cover = modules?.moduleDynamic?.major?.opus?.pics?.first.url; + + /// 投稿 + case 'DYNAMIC_TYPE_AV': + cover = modules?.moduleDynamic?.major?.archive?.cover; + + /// 专栏文章 + case 'DYNAMIC_TYPE_ARTICLE': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC': + cover = ''; + + /// 纯文字动态 + case 'DYNAMIC_TYPE_WORD': + cover = ''; + + /// 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + cover = ''; + + /// 合集查看 + case 'DYNAMIC_TYPE_UGC_SEASON': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC_UNION': + cover = modules?.moduleDynamic?.major?.pgc?.cover; + + default: + cover = ''; + } + + /// 专栏文章 + case 'DYNAMIC_TYPE_ARTICLE': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC': + cover = ''; + + /// 纯文字动态 + case 'DYNAMIC_TYPE_WORD': + cover = ''; + + /// 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + cover = ''; + + /// 合集查看 + case 'DYNAMIC_TYPE_UGC_SEASON': + cover = ''; + + /// 番剧查看 + case 'DYNAMIC_TYPE_PGC_UNION': + cover = ''; + + default: + cover = ''; + } + return Container( + width: double.infinity, + height: 95, + margin: const EdgeInsets.fromLTRB(12, 0, 12, 14), + decoration: BoxDecoration( + color: + Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4), + borderRadius: BorderRadius.circular(6), + border: Border( + left: BorderSide( + width: 4, + color: Theme.of(context).colorScheme.primary.withOpacity(0.8)), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '@${widget.item.modules!.moduleAuthor!.name}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(height: 8), + Row( + children: [ + NetworkImgLayer( + src: cover ?? '', + width: 34, + height: 34, + type: 'emote', + ), + const SizedBox(width: 10), + Expanded( + child: Text.rich( + style: const TextStyle(height: 0), + richNode(widget.item, context), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + // Text(data) + ], + ) + ], + ), + ), + ); + } + + // 动态转发 + void forwardHandler() async { + showModalBottomSheet( + context: context, + enableDrag: false, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return Obx( + () => AnimatedContainer( + duration: Durations.medium1, + onEnd: () async { + if (isExpand.value) { + await Future.delayed(const Duration(milliseconds: 80)); + myFocusNode.requestFocus(); + } + }, + height: height.value + MediaQuery.of(context).padding.bottom, + child: Column( + children: [ + AnimatedContainer( + duration: Durations.medium1, + height: isExpand.value ? statusHeight : 0, + ), + Padding( + padding: EdgeInsets.fromLTRB( + isExpand.value ? 10 : 16, + 10, + isExpand.value ? 14 : 12, + 0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (isExpand.value) ...[ + IconButton( + onPressed: () => togglePanelState(false), + icon: const Icon(Icons.close), + ), + Text( + '转发动态', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ) + ] else ...[ + const Text( + '转发动态', + style: TextStyle(fontWeight: FontWeight.bold), + ) + ], + isExpand.value + ? FilledButton( + onPressed: () => dynamicForward('forward'), + child: const Text('转发'), + ) + : TextButton( + onPressed: () {}, + child: const Text('立即转发'), + ) + ], + ), + ), + if (!isExpand.value) ...[ + GestureDetector( + onTap: () => togglePanelState(true), + behavior: HitTestBehavior.translucent, + child: Container( + width: double.infinity, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(16, 0, 10, 14), + child: Text( + '说点什么吧', + textAlign: TextAlign.start, + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + ), + ] else ...[ + Padding( + padding: const EdgeInsets.fromLTRB(16, 10, 16, 0), + child: TextField( + maxLines: 5, + focusNode: myFocusNode, + controller: _inputController, + onChanged: (value) { + setState(() { + _inputText = value; + }); + }, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '说点什么吧', + ), + ), + ), + ], + dynamicPreview(), + if (!isExpand.value) ...[ + const Divider(thickness: 0.1, height: 1), + ListTile( + onTap: () => Get.back(), + minLeadingWidth: 0, + dense: true, + title: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + textAlign: TextAlign.center, + ), + ), + ] + ], + ), + ), + ); + }, + ); + } + + togglePanelState(status) { + if (!status) { + Get.back(); + height.value = defaultHeight; + _inputText = ''; + _inputController.clear(); + } else { + height.value = Get.size.height; + } + isExpand.value = !(isExpand.value); + } + + dynamicForward(String type) async { + String dynamicId = widget.item.idStr!; + var res = await DynamicsHttp.dynamicCreate( + dynIdStr: dynamicId, + mid: _dynamicsController.userInfo.mid, + rawText: _inputText, + scene: 4, + ); + if (res['status']) { + SmartDialog.showToast(type == 'forward' ? '转发成功' : '发布成功'); + togglePanelState(false); + } + } + + @override + void dispose() { + myFocusNode.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { var color = Theme.of(context).colorScheme.outline; var primary = Theme.of(context).colorScheme.primary; + height.value = defaultHeight; + print('height.value: ${height.value}'); return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Expanded( flex: 1, child: TextButton.icon( - onPressed: () {}, + onPressed: forwardHandler, icon: const Icon( FontAwesomeIcons.shareFromSquare, size: 16, diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index c85cad45..d273a1a6 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,15 +1,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/pages/dynamics/index.dart'; +import '../../../models/dynamics/result.dart'; import 'action_panel.dart'; import 'author_panel.dart'; import 'content_panel.dart'; import 'forward_panel.dart'; class DynamicPanel extends StatelessWidget { - final dynamic item; + final DynamicItemModel item; final String? source; - DynamicPanel({this.item, this.source, Key? key}) : super(key: key); + DynamicPanel({required this.item, this.source, Key? key}) : super(key: key); final DynamicsController _dynamicsController = Get.put(DynamicsController()); @override @@ -41,8 +42,8 @@ class DynamicPanel extends StatelessWidget { padding: const EdgeInsets.fromLTRB(12, 12, 12, 8), child: AuthorPanel(item: item), ), - if (item!.modules!.moduleDynamic!.desc != null || - item!.modules!.moduleDynamic!.major != null) + if (item.modules!.moduleDynamic!.desc != null || + item.modules!.moduleDynamic!.major != null) Content(item: item, source: source), forWard(item, context, _dynamicsController, source), const SizedBox(height: 2), diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index a5f94525..2307d303 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -10,10 +10,11 @@ import 'package:pilipala/utils/storage.dart'; class FavController extends GetxController { final ScrollController scrollController = ScrollController(); Rx favFolderData = FavFolderData().obs; + RxList favFolderList = [].obs; Box userInfoCache = GStrorage.userInfo; UserInfoData? userInfo; int currentPage = 1; - int pageSize = 10; + int pageSize = 60; RxBool hasMore = true.obs; Future queryFavFolder({type = 'init'}) async { @@ -32,9 +33,10 @@ class FavController extends GetxController { if (res['status']) { if (type == 'init') { favFolderData.value = res['data']; + favFolderList.value = res['data'].list; } else { if (res['data'].list.isNotEmpty) { - favFolderData.value.list!.addAll(res['data'].list); + favFolderList.addAll(res['data'].list); favFolderData.update((val) {}); } } @@ -49,4 +51,13 @@ class FavController extends GetxController { Future onLoad() async { queryFavFolder(type: 'onload'); } + + removeFavFolder({required int mediaIds}) async { + for (var i in favFolderList) { + if (i.id == mediaIds) { + favFolderList.remove(i); + break; + } + } + } } diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index b980914a..424a885d 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -62,11 +62,10 @@ class _FavPageState extends State { return Obx( () => ListView.builder( controller: scrollController, - itemCount: _favController.favFolderData.value.list!.length, + itemCount: _favController.favFolderList.length, itemBuilder: (context, index) { return FavItem( - favFolderItem: - _favController.favFolderData.value.list![index]); + favFolderItem: _favController.favFolderList[index]); }, ), ); diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 08730d7b..3c44ec9d 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -13,14 +13,16 @@ class FavItem extends StatelessWidget { Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(favFolderItem.fid); return InkWell( - onTap: () => Get.toNamed( - '/favDetail', - arguments: favFolderItem, - parameters: { - 'heroTag': heroTag, - 'mediaId': favFolderItem.id.toString(), - }, - ), + onTap: () async { + Get.toNamed( + '/favDetail', + arguments: favFolderItem, + parameters: { + 'heroTag': heroTag, + 'mediaId': favFolderItem.id.toString(), + }, + ); + }, child: Padding( padding: const EdgeInsets.fromLTRB(12, 7, 12, 7), child: LayoutBuilder( diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 55d5b884..7af398e8 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -1,9 +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/http/video.dart'; import 'package:pilipala/models/user/fav_detail.dart'; import 'package:pilipala/models/user/fav_folder.dart'; +import 'package:pilipala/pages/fav/index.dart'; class FavDetailController extends GetxController { FavFolderItemData? item; @@ -74,4 +76,41 @@ class FavDetailController extends GetxController { onLoad() { queryUserFavFolderDetail(type: 'onLoad'); } + + onDelFavFolder() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('确定删除这个收藏夹吗?'), + actions: [ + TextButton( + onPressed: () async { + SmartDialog.dismiss(); + }, + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await UserHttp.delFavFolder(mediaIds: mediaId!); + SmartDialog.dismiss(); + SmartDialog.showToast(res['status'] ? '操作成功' : res['msg']); + if (res['status']) { + FavController favController = Get.find(); + await favController.removeFavFolder(mediaIds: mediaId!); + Get.back(); + } + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 74faa829..1bf5cb6f 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -53,6 +53,7 @@ class _FavDetailPageState extends State { @override void dispose() { _controller.dispose(); + titleStreamC.close(); super.dispose(); } @@ -100,11 +101,19 @@ class _FavDetailPageState extends State { Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'), icon: const Icon(Icons.search_outlined), ), - // IconButton( - // onPressed: () {}, - // icon: const Icon(Icons.more_vert), - // ), - const SizedBox(width: 6), + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined), + position: PopupMenuPosition.under, + onSelected: (String type) {}, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + onTap: () => _favDetailController.onDelFavFolder(), + value: 'pause', + child: const Text('删除收藏夹'), + ), + ], + ), + const SizedBox(width: 14), ], flexibleSpace: FlexibleSpaceBar( background: Container( diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index ca70e1c4..f197fdfa 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -114,4 +114,10 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { defaultSearch.value = res.data['data']['name']; } } + + @override + void onClose() { + searchBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index c2e5c322..a77d9304 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -101,4 +101,10 @@ class MainController extends GetxController { selectedIndex = defaultIndex != -1 ? defaultIndex : 0; pages = navigationBars.map((e) => e['page']).toList(); } + + @override + void onClose() { + bottomBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 015750db..b6648647 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -54,6 +54,7 @@ class _MemberPageState extends State @override void dispose() { _extendNestCtr.removeListener(() {}); + appbarStream.close(); super.dispose(); } diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index 68aa72d7..2e093bcc 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/pages/member_dynamics/index.dart'; import 'package:pilipala/utils/utils.dart'; import '../../common/widgets/http_error.dart'; +import '../../models/dynamics/result.dart'; import '../dynamics/widgets/dynamic_panel.dart'; class MemberDynamicsPage extends StatefulWidget { @@ -66,7 +67,8 @@ class _MemberDynamicsPageState extends State { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data != null) { Map data = snapshot.data as Map; - List list = _memberDynamicController.dynamicsList; + RxList list = + _memberDynamicController.dynamicsList; if (data['status']) { return Obx( () => list.isNotEmpty diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index fa906fc9..64395ec7 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -54,4 +54,10 @@ class RankController extends GetxController with GetTickerProviderStateMixin { vsync: this, ); } + + @override + void onClose() { + searchBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 2c219e58..63352429 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -53,6 +53,7 @@ class _SubDetailPageState extends State { @override void dispose() { _controller.dispose(); + titleStreamC.close(); super.dispose(); } diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index a94b6071..029e015a 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/emote.dart'; @@ -40,6 +41,8 @@ class _VideoReplyNewDialogState extends State double keyboardHeight = 0.0; // 键盘高度 final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 String toolbarType = 'input'; + RxBool isForward = false.obs; + RxBool showForward = false.obs; @override void initState() { @@ -52,6 +55,10 @@ class _VideoReplyNewDialogState extends State _autoFocus(); // 监听聚焦状态 _focuslistener(); + final String routePath = Get.currentRoute; + if (routePath.startsWith('/video')) { + showForward.value = true; + } } _autoFocus() async { @@ -88,6 +95,16 @@ class _VideoReplyNewDialogState extends State Get.back(result: { 'data': ReplyItemModel.fromJson(result['data']['reply'], ''), }); + + /// 投稿、番剧页面 + if (isForward.value) { + await DynamicsHttp.dynamicCreate( + mid: 0, + rawText: message, + oid: widget.oid!, + scene: 5, + ); + } } else { SmartDialog.showToast(result['msg']); } @@ -145,7 +162,6 @@ class _VideoReplyNewDialogState extends State double _keyboardHeight = EdgeInsets.fromViewPadding( View.of(context).viewInsets, View.of(context).devicePixelRatio) .bottom; - print('_keyboardHeight: $_keyboardHeight'); return Container( clipBehavior: Clip.hardEdge, decoration: BoxDecoration( @@ -225,9 +241,37 @@ class _VideoReplyNewDialogState extends State toolbarType: toolbarType, selected: toolbarType == 'emote', ), + const SizedBox(width: 6), + Obx( + () => showForward.value + ? TextButton.icon( + onPressed: () { + isForward.value = !isForward.value; + }, + icon: Icon( + isForward.value + ? Icons.check_box + : Icons.check_box_outline_blank, + size: 22), + label: const Text('转发到动态'), + style: ButtonStyle( + foregroundColor: MaterialStateProperty.all( + isForward.value + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + ), + ) + : const SizedBox(), + ), const Spacer(), - TextButton( - onPressed: () => submitReplyAdd(), child: const Text('发送')) + SizedBox( + height: 36, + child: FilledButton( + onPressed: () => submitReplyAdd(), + child: const Text('发送'), + ), + ), ], ), ), diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index bb9d556f..9009df6f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -20,7 +20,7 @@ class PiliSchame { /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { if (value != null) { - _fullPathPush(value); + _routePush(value); } }); @@ -37,7 +37,6 @@ class PiliSchame { final String scheme = value.scheme; final String host = value.host; final String path = value.path; - if (scheme == 'bilibili') { if (host == 'root') { Navigator.popUntil( @@ -85,6 +84,14 @@ class PiliSchame { } } else if (host == 'search') { Get.toNamed('/searchResult', parameters: {'keyword': ''}); + } else if (host == 'article') { + final String id = path.split('/').last.split('?').first; + Get.toNamed('/htmlRender', parameters: { + 'url': 'https://www.bilibili.com/read/cv$id', + 'title': 'cv$id', + 'id': 'cv$id', + 'dynamicType': 'read' + }); } } if (scheme == 'https') { @@ -155,9 +162,14 @@ class PiliSchame { final String host = value.host!; final String? path = value.path; Map? query = value.query; - RegExp regExp = RegExp(r'^(www\.)?m?\.(bilibili\.com)$'); + RegExp regExp = RegExp(r'^((www\.)|(m\.))?bilibili\.com$'); if (regExp.hasMatch(host)) { - print('bilibili.com'); + print('bilibili.com host: $host'); + print('bilibili.com path: $path'); + final String lastPathSegment = path!.split('/').last; + if (lastPathSegment.contains('BV')) { + _videoPush(null, lastPathSegment); + } } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); Get.toNamed( @@ -226,6 +238,13 @@ class PiliSchame { break; case 'read': print('专栏'); + String id = 'cv${matchNum(query!['id']!).first}'; + Get.toNamed('/htmlRender', parameters: { + 'url': value.dataString!, + 'title': '', + 'id': id, + 'dynamicType': 'read' + }); break; case 'space': print('个人空间');