From 3edac65ae8731a94e519cd8d8c9bdd7d22bced1a Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Mon, 26 May 2025 17:11:52 +0800 Subject: [PATCH] opt pub panel Signed-off-by: bggRGjQaUbCoE --- lib/common/widgets/dialog/report.dart | 4 +- lib/common/widgets/image/image_save.dart | 4 +- lib/http/api.dart | 3 + lib/http/dynamics.dart | 93 ++++++ lib/http/msg.dart | 83 ----- lib/http/search.dart | 26 ++ lib/models/topic_pub_search/data.dart | 34 ++ lib/models/topic_pub_search/new_topic.dart | 9 + lib/models/topic_pub_search/page_info.dart | 11 + lib/models/topic_pub_search/topic_item.dart | 30 ++ lib/pages/about/view.dart | 2 +- lib/pages/common/common_publish_page.dart | 17 +- lib/pages/dynamics/view.dart | 11 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- lib/pages/dynamics_create/view.dart | 294 +++++++++++++----- lib/pages/dynamics_repost/view.dart | 262 +++++++++------- .../dynamics_select_topic/controller.dart | 38 +++ lib/pages/dynamics_select_topic/view.dart | 200 ++++++++++++ lib/pages/fav_create/view.dart | 2 +- lib/pages/follow/view.dart | 2 +- lib/pages/live_room/send_danmaku/view.dart | 14 +- lib/pages/live_room/view.dart | 2 +- lib/pages/member_profile/view.dart | 2 +- lib/pages/save_panel/view.dart | 3 +- .../search_panel/article/controller.dart | 2 +- lib/pages/search_panel/user/controller.dart | 2 +- lib/pages/search_panel/video/controller.dart | 2 +- lib/pages/search_trending/view.dart | 2 +- lib/pages/setting/pages/display_mode.dart | 2 +- lib/pages/setting/pages/play_speed_set.dart | 4 +- lib/pages/setting/slide_color_picker.dart | 3 +- lib/pages/setting/widgets/model.dart | 2 +- lib/pages/share/view.dart | 2 + lib/pages/sponsor_block/view.dart | 2 +- lib/pages/video/controller.dart | 8 +- lib/pages/video/introduction/pgc/view.dart | 2 +- lib/pages/video/introduction/ugc/view.dart | 2 +- .../introduction/ugc/widgets/action_item.dart | 2 +- lib/pages/video/post_panel/view.dart | 6 +- .../video/reply/widgets/reply_item_grpc.dart | 4 +- lib/pages/video/reply_new/view.dart | 11 +- lib/pages/video/send_danmaku/view.dart | 15 +- lib/pages/video/view.dart | 2 +- lib/pages/video/widgets/header_control.dart | 2 +- lib/pages/whisper_block/view.dart | 2 + lib/pages/whisper_detail/view.dart | 4 +- lib/utils/feed_back.dart | 2 +- lib/utils/page_utils.dart | 4 +- lib/utils/request_utils.dart | 2 +- 49 files changed, 879 insertions(+), 360 deletions(-) create mode 100644 lib/models/topic_pub_search/data.dart create mode 100644 lib/models/topic_pub_search/new_topic.dart create mode 100644 lib/models/topic_pub_search/page_info.dart create mode 100644 lib/models/topic_pub_search/topic_item.dart create mode 100644 lib/pages/dynamics_select_topic/controller.dart create mode 100644 lib/pages/dynamics_select_topic/view.dart diff --git a/lib/common/widgets/dialog/report.dart b/lib/common/widgets/dialog/report.dart index 1286b076..46c7475a 100644 --- a/lib/common/widgets/dialog/report.dart +++ b/lib/common/widgets/dialog/report.dart @@ -141,9 +141,7 @@ class _ReasonFieldState extends State { border: OutlineInputBorder(), contentPadding: EdgeInsets.all(10), ), - onChanged: (value) { - widget.onChanged(value); - }, + onChanged: widget.onChanged, validator: widget._validator, ), ); diff --git a/lib/common/widgets/image/image_save.dart b/lib/common/widgets/image/image_save.dart index 2fea87e2..46384298 100644 --- a/lib/common/widgets/image/image_save.dart +++ b/lib/common/widgets/image/image_save.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; @@ -12,7 +10,7 @@ void imageSaveDialog({ required String? title, required String? cover, }) { - final double imgWidth = min(Get.width, Get.height) - 8 * 2; + final double imgWidth = Get.mediaQuery.size.shortestSide - 8 * 2; SmartDialog.show( animationType: SmartAnimationType.centerScale_otherSlide, builder: (context) { diff --git a/lib/http/api.dart b/lib/http/api.dart index 4f01f21a..5c7402fc 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -869,4 +869,7 @@ class Api { static const String pgcReviewMod = '/pgc/review/short/modify'; static const String pgcReviewDel = '/pgc/review/short/del'; + + static const String topicPubSearch = + '${HttpString.appBaseUrl}/x/topic/pub/search'; } diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 41fd8726..06ed89e3 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -1,8 +1,10 @@ +import 'package:PiliPlus/common/widgets/pair.dart'; import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart'; +import 'package:PiliPlus/models/common/reply/reply_option_type.dart'; import 'package:PiliPlus/models/dynamics/article_list/data.dart'; import 'package:PiliPlus/models/dynamics/dyn_topic_feed/topic_card_list.dart'; import 'package:PiliPlus/models/dynamics/dyn_topic_top/top_details.dart'; @@ -12,6 +14,7 @@ import 'package:PiliPlus/models/dynamics/vote_model.dart'; import 'package:PiliPlus/models/space_article/item.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; @@ -131,6 +134,96 @@ class DynamicsHttp { } } + static Future createDynamic({ + dynamic mid, + dynamic dynIdStr, // repost dyn + dynamic rid, // repost video + dynamic dynType, + dynamic rawText, + List? pics, + int? publishTime, + ReplyOptionType? replyOption, + int? privatePub, + List>? extraContent, + Pair? topic, + String? title, + }) async { + var res = await Request().post( + Api.createDynamic, + queryParameters: { + 'platform': 'web', + 'csrf': Accounts.main.csrf, + '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": "", + }, + if (extraContent != null) ...extraContent, + ], + if (title?.isNotEmpty == true) 'title': title, + }, + if (privatePub != null || replyOption != null || publishTime != null) + "option": { + if (privatePub != null) 'private_pub': privatePub, + if (publishTime != null) "timer_pub_time": publishTime, + if (replyOption == ReplyOptionType.close) + "close_comment": 1 + else if (replyOption == ReplyOptionType.choose) + "up_choose_comment": 1, + }, + "scene": rid != null + ? 5 + : dynIdStr != null + ? 4 + : pics != null + ? 2 + : 1, + if (pics != null) 'pics': pics, + "attach_card": null, + "upload_id": + "${rid != null ? 0 : mid}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}_${Utils.random.nextInt(9000) + 1000}", + "meta": { + "app_meta": {"from": "create.dynamic.web", "mobi_app": "web"} + }, + if (topic != null) + "topic": { + "id": topic.first, + "name": topic.second, + "from_source": "dyn.web.list", + "from_topic_id": 0, + } + }, + if (dynIdStr != null || rid != null) + "web_repost_src": { + if (dynIdStr != null) "dyn_id_str": dynIdStr, + if (rid != null) + "revs_id": { + "dyn_type": dynType, + "rid": rid, + } + } + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'msg': res.data['message'], + }; + } + } + // static Future dynamicDetail({ dynamic id, diff --git a/lib/http/msg.dart b/lib/http/msg.dart index d66b774a..52fabf3f 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,10 +1,7 @@ -import 'dart:math'; - import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/models/common/reply/reply_option_type.dart'; import 'package:PiliPlus/models/msg/account.dart'; import 'package:PiliPlus/models/msg/im_user_infos/datum.dart'; import 'package:PiliPlus/models/msg/msg_dnd/uid_setting.dart'; @@ -106,86 +103,6 @@ class MsgHttp { } } - static Future createDynamic({ - dynamic mid, - dynamic dynIdStr, // repost dyn - dynamic rid, // repost video - dynamic dynType, - dynamic rawText, - List? pics, - int? publishTime, - ReplyOptionType? replyOption, - int? privatePub, - List>? extraContent, - }) async { - var res = await Request().post( - Api.createDynamic, - queryParameters: { - 'platform': 'web', - 'csrf': Accounts.main.csrf, - '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": "", - }, - if (extraContent != null) ...extraContent, - ] - }, - if (privatePub != null || replyOption != null || publishTime != null) - "option": { - if (privatePub != null) 'private_pub': privatePub, - if (publishTime != null) "timer_pub_time": publishTime, - if (replyOption == ReplyOptionType.close) - "close_comment": 1 - else if (replyOption == ReplyOptionType.choose) - "up_choose_comment": 1, - }, - "scene": rid != null - ? 5 - : dynIdStr != null - ? 4 - : pics != null - ? 2 - : 1, - if (pics != null) 'pics': pics, - "attach_card": null, - "upload_id": - "${rid != null ? 0 : mid}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}_${Random().nextInt(9000) + 1000}", - "meta": { - "app_meta": {"from": "create.dynamic.web", "mobi_app": "web"} - } - }, - if (dynIdStr != null || rid != null) - "web_repost_src": { - if (dynIdStr != null) "dyn_id_str": dynIdStr, - if (rid != null) - "revs_id": { - "dyn_type": dynType, - "rid": rid, - } - } - }, - ); - if (res.data['code'] == 0) { - return { - 'status': true, - 'data': res.data['data'], - }; - } else { - return { - 'status': false, - 'msg': res.data['message'], - }; - } - } - static Future uploadImage({ required dynamic path, required String bucket, diff --git a/lib/http/search.dart b/lib/http/search.dart index fdcf412d..9a287717 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -8,6 +8,7 @@ import 'package:PiliPlus/models/pgc/info.dart'; import 'package:PiliPlus/models/search/result.dart'; import 'package:PiliPlus/models/search/search_trending/trending_data.dart'; import 'package:PiliPlus/models/search/suggest.dart'; +import 'package:PiliPlus/models/topic_pub_search/data.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; @@ -259,4 +260,29 @@ class SearchHttp { return Error(res.data['message']); } } + + static Future> topicPubSearch({ + required String keywords, + String content = '', + required int pageNum, + }) async { + final res = await Request().get( + Api.topicPubSearch, + queryParameters: { + 'keywords': keywords, + 'content': content, + if (pageNum == 1) ...{ + 'page_size': 20, + 'page_num': 1, + } else + 'offset': 20 * (pageNum - 1), + 'web_location': 333.1365, + }, + ); + if (res.data['code'] == 0) { + return Success(TopicPubSearchData.fromJson(res.data['data'])); + } else { + return Error(res.data['message']); + } + } } diff --git a/lib/models/topic_pub_search/data.dart b/lib/models/topic_pub_search/data.dart new file mode 100644 index 00000000..e82dc995 --- /dev/null +++ b/lib/models/topic_pub_search/data.dart @@ -0,0 +1,34 @@ +import 'package:PiliPlus/models/topic_pub_search/new_topic.dart'; +import 'package:PiliPlus/models/topic_pub_search/page_info.dart'; +import 'package:PiliPlus/models/topic_pub_search/topic_item.dart'; + +class TopicPubSearchData { + NewTopic? newTopic; + bool? hasCreateJurisdiction; + List? topicItems; + String? requestId; + PageInfo? pageInfo; + + TopicPubSearchData({ + this.newTopic, + this.hasCreateJurisdiction, + this.topicItems, + this.requestId, + this.pageInfo, + }); + + factory TopicPubSearchData.fromJson(Map json) => + TopicPubSearchData( + newTopic: json['new_topic'] == null + ? null + : NewTopic.fromJson(json['new_topic'] as Map), + hasCreateJurisdiction: json['has_create_jurisdiction'] as bool?, + topicItems: (json['topic_items'] as List?) + ?.map((e) => TopicPubSearchItem.fromJson(e as Map)) + .toList(), + requestId: json['request_id'] as String?, + pageInfo: json['page_info'] == null + ? null + : PageInfo.fromJson(json['page_info'] as Map), + ); +} diff --git a/lib/models/topic_pub_search/new_topic.dart b/lib/models/topic_pub_search/new_topic.dart new file mode 100644 index 00000000..8b1e67a1 --- /dev/null +++ b/lib/models/topic_pub_search/new_topic.dart @@ -0,0 +1,9 @@ +class NewTopic { + String? name; + + NewTopic({this.name}); + + factory NewTopic.fromJson(Map json) => NewTopic( + name: json['name'] as String?, + ); +} diff --git a/lib/models/topic_pub_search/page_info.dart b/lib/models/topic_pub_search/page_info.dart new file mode 100644 index 00000000..29191737 --- /dev/null +++ b/lib/models/topic_pub_search/page_info.dart @@ -0,0 +1,11 @@ +class PageInfo { + int? offset; + bool? hasMore; + + PageInfo({this.offset, this.hasMore}); + + factory PageInfo.fromJson(Map json) => PageInfo( + offset: json['offset'] as int?, + hasMore: json['has_more'] as bool?, + ); +} diff --git a/lib/models/topic_pub_search/topic_item.dart b/lib/models/topic_pub_search/topic_item.dart new file mode 100644 index 00000000..0b1043e8 --- /dev/null +++ b/lib/models/topic_pub_search/topic_item.dart @@ -0,0 +1,30 @@ +class TopicPubSearchItem { + int? id; + String? name; + int? view; + int? discuss; + String? statDesc; + String? description; + bool? showInteractData; + + TopicPubSearchItem({ + this.id, + this.name, + this.view, + this.discuss, + this.statDesc, + this.description, + this.showInteractData, + }); + + factory TopicPubSearchItem.fromJson(Map json) => + TopicPubSearchItem( + id: json['id'] as int?, + name: json['name'] as String?, + view: json['view'] as int?, + discuss: json['discuss'] as int?, + statDesc: json['stat_desc'] as String?, + description: json['description'] as String?, + showInteractData: json['show_interact_data'] as bool?, + ); +} diff --git a/lib/pages/about/view.dart b/lib/pages/about/view.dart index 51a9dbe6..aeee0f9f 100644 --- a/lib/pages/about/view.dart +++ b/lib/pages/about/view.dart @@ -13,7 +13,7 @@ import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show Clipboard, ClipboardData; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; diff --git a/lib/pages/common/common_publish_page.dart b/lib/pages/common/common_publish_page.dart index bf77bf30..33ed6eb6 100644 --- a/lib/pages/common/common_publish_page.dart +++ b/lib/pages/common/common_publish_page.dart @@ -40,10 +40,9 @@ abstract class CommonPublishPageState late final controller = ChatBottomPanelContainerController(); late final editController = TextEditingController(text: widget.initialValue); - PanelType currentPanelType = PanelType.none; + Rx panelType = PanelType.none.obs; late final RxBool readOnly = false.obs; late final RxBool enablePublish = false.obs; - late final RxBool selectKeyboard = true.obs; late final imagePicker = ImagePicker(); late final RxList pathList = [].obs; @@ -83,7 +82,9 @@ abstract class CommonPublishPageState @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { - if (mounted && widget.autofocus && selectKeyboard.value) { + if (mounted && + widget.autofocus && + panelType.value == PanelType.keyboard) { WidgetsBinding.instance.addPostFrameCallback((_) { if (focusNode.hasFocus) { focusNode.unfocus(); @@ -249,22 +250,22 @@ abstract class CommonPublishPageState } }, onPanelTypeChange: (panelType, data) { - debugPrint('panelType: $panelType'); + // debugPrint('panelType: $panelType'); switch (panelType) { case ChatBottomPanelType.none: - currentPanelType = PanelType.none; + this.panelType.value = PanelType.none; break; case ChatBottomPanelType.keyboard: - currentPanelType = PanelType.keyboard; + this.panelType.value = PanelType.keyboard; break; case ChatBottomPanelType.other: if (data == null) return; switch (data) { case PanelType.emoji: - currentPanelType = PanelType.emoji; + this.panelType.value = PanelType.emoji; break; default: - currentPanelType = PanelType.none; + this.panelType.value = PanelType.none; break; } break; diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 32871b16..b728f1e6 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -44,7 +44,16 @@ class _DynamicsPageState extends State context: context, useSafeArea: true, isScrollControlled: true, - builder: (context) => const CreateDynPanel(), + builder: (context) => DraggableScrollableSheet( + snap: true, + expand: false, + initialChildSize: 1, + minChildSize: 0, + maxChildSize: 1, + snapSizes: const [1], + builder: (context, scrollController) => + CreateDynPanel(scrollController: scrollController), + ), ); } }, diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 3348f5be..9f9b4e71 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -239,7 +239,7 @@ class AuthorPanel extends StatelessWidget { useSafeArea: true, isScrollControlled: true, constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (context1) { final theme = Theme.of(context); diff --git a/lib/pages/dynamics_create/view.dart b/lib/pages/dynamics_create/view.dart index bf0e065b..b84a721c 100644 --- a/lib/pages/dynamics_create/view.dart +++ b/lib/pages/dynamics_create/view.dart @@ -1,13 +1,22 @@ +import 'dart:math'; + +import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/button/toolbar_icon_button.dart'; -import 'package:PiliPlus/http/msg.dart'; +import 'package:PiliPlus/common/widgets/custom_icon.dart'; +import 'package:PiliPlus/common/widgets/pair.dart'; +import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/models/common/publish_panel_type.dart'; import 'package:PiliPlus/models/common/reply/reply_option_type.dart'; +import 'package:PiliPlus/models/topic_pub_search/topic_item.dart'; import 'package:PiliPlus/pages/common/common_publish_page.dart'; +import 'package:PiliPlus/pages/dynamics_select_topic/controller.dart'; +import 'package:PiliPlus/pages/dynamics_select_topic/view.dart'; import 'package:PiliPlus/pages/emote/controller.dart'; import 'package:PiliPlus/pages/emote/view.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; @@ -16,21 +25,29 @@ class CreateDynPanel extends CommonPublishPage { const CreateDynPanel({ super.key, super.imageLengthLimit = 18, + this.scrollController, }); + final ScrollController? scrollController; + @override State createState() => _CreateDynPanelState(); } class _CreateDynPanelState extends CommonPublishPageState { - bool _isPrivate = false; - DateTime? _publishTime; - ReplyOptionType _replyOption = ReplyOptionType.allow; + final RxBool _isPrivate = false.obs; + final Rx _publishTime = Rx(null); + final Rx _replyOption = ReplyOptionType.allow.obs; + final _titleEditCtr = TextEditingController(); + Rx?> topic = Rx?>(null); @override void dispose() { + _titleEditCtr.dispose(); try { - Get.delete(); + Get + ..delete() + ..delete(); } catch (_) {} super.dispose(); } @@ -46,30 +63,130 @@ class _CreateDynPanelState extends CommonPublishPageState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, + child: ListView( + padding: EdgeInsets.zero, + controller: widget.scrollController, + physics: const ClampingScrollPhysics(), children: [ - Flexible( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _buildEditWidget, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Obx( + () { + final hasTopic = topic.value != null; + return Row( + spacing: 10, + children: [ + TextButton( + style: TextButton.styleFrom( + overlayColor: + hasTopic ? Colors.transparent : null, + splashFactory: + hasTopic ? NoSplash.splashFactory : null, + shape: hasTopic + ? null + : RoundedRectangleBorder( + side: BorderSide( + color: hasTopic + ? Colors.transparent + : theme.colorScheme.outline + .withValues(alpha: 0.2), + ), + borderRadius: const BorderRadius.all( + Radius.circular(25), + ), + ), + minimumSize: Size.zero, + padding: hasTopic + ? const EdgeInsets.symmetric(vertical: 12) + : const EdgeInsets.all(12), + visualDensity: VisualDensity.compact, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: _onSelectTopic, + child: Text.rich( + TextSpan( + children: [ + WidgetSpan( + child: Padding( + padding: const EdgeInsets.only(right: 5), + child: Icon( + CustomIcon.topic_tag, + size: 18, + color: hasTopic + ? null + : theme.colorScheme.outline, + ), + ), + ), + TextSpan( + text: + hasTopic ? topic.value!.second : '选择话题', + style: TextStyle( + color: hasTopic + ? null + : theme.colorScheme.outline, + ), + ), + ], + ), + ), + ), + if (hasTopic) + iconButton( + size: 22, + iconSize: 16, + context: context, + icon: Icons.clear, + bgColor: theme.colorScheme.onInverseSurface, + iconColor: theme.colorScheme.onSurfaceVariant, + onPressed: () => topic.value = null, + ), + ], + ); + }, ), ), + const SizedBox(height: 5), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TextField( + controller: _titleEditCtr, + style: const TextStyle(fontWeight: FontWeight.bold), + decoration: InputDecoration( + hintText: '标题,选填20字', + isDense: true, + contentPadding: EdgeInsets.zero, + border: const OutlineInputBorder( + gapPadding: 0, + borderSide: BorderSide.none, + ), + hintStyle: TextStyle( + fontWeight: FontWeight.bold, + color: theme.colorScheme.outline.withValues(alpha: 0.7), + ), + ), + inputFormatters: [LengthLimitingTextInputFormatter(20)], + ), + ), + const SizedBox(height: 5), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: _buildEditWidget(theme), + ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _buildPubtimeWidget, + Obx(() => _buildPubtimeWidget), Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildReplyOptionWidget(theme), + Obx(() => _buildReplyOptionWidget(theme)), const SizedBox(height: 5), - _buildPrivateWidget(theme), + Obx(() => _buildPrivateWidget(theme)), ], ), ], @@ -77,7 +194,6 @@ class _CreateDynPanelState extends CommonPublishPageState { ), const SizedBox(height: 10), _buildImageList(theme), - const SizedBox(height: 2), ], ), ), @@ -186,7 +302,7 @@ class _CreateDynPanelState extends CommonPublishPageState { ), visualDensity: VisualDensity.compact, ), - child: Text(_publishTime == null ? '发布' : '定时发布'), + child: Text(_publishTime.value == null ? '发布' : '定时发布'), ), ), ), @@ -196,20 +312,17 @@ class _CreateDynPanelState extends CommonPublishPageState { ); Widget _buildPrivateWidget(ThemeData theme) { - final color = - _isPrivate ? theme.colorScheme.error : theme.colorScheme.secondary; - return PopupMenuButton( - initialValue: _isPrivate, + final color = _isPrivate.value + ? theme.colorScheme.error + : theme.colorScheme.secondary; + return PopupMenuButton( + initialValue: _isPrivate.value, onOpened: controller.keepChatPanel, - onSelected: (value) { - setState(() { - _isPrivate = value; - }); - }, + onSelected: (value) => _isPrivate.value = value, itemBuilder: (context) => List.generate( 2, (index) => PopupMenuItem( - enabled: _publishTime != null && index == 1 ? false : true, + enabled: _publishTime.value != null && index == 1 ? false : true, value: index == 0 ? false : true, child: Row( mainAxisSize: MainAxisSize.min, @@ -231,12 +344,12 @@ class _CreateDynPanelState extends CommonPublishPageState { children: [ Icon( size: 19, - _isPrivate ? Icons.visibility_off : Icons.visibility, + _isPrivate.value ? Icons.visibility_off : Icons.visibility, color: color, ), const SizedBox(width: 4), Text( - _isPrivate ? '仅自己可见' : '所有人可见', + _isPrivate.value ? '仅自己可见' : '所有人可见', style: TextStyle( height: 1, color: color, @@ -255,17 +368,13 @@ class _CreateDynPanelState extends CommonPublishPageState { } Widget _buildReplyOptionWidget(ThemeData theme) { - final color = _replyOption == ReplyOptionType.close + final color = _replyOption.value == ReplyOptionType.close ? theme.colorScheme.error : theme.colorScheme.secondary; - return PopupMenuButton( - initialValue: _replyOption, + return PopupMenuButton( + initialValue: _replyOption.value, onOpened: controller.keepChatPanel, - onSelected: (item) { - setState(() { - _replyOption = item; - }); - }, + onSelected: (item) => _replyOption.value = item, itemBuilder: (context) => ReplyOptionType.values .map( (item) => PopupMenuItem( @@ -291,12 +400,12 @@ class _CreateDynPanelState extends CommonPublishPageState { children: [ Icon( size: 19, - _replyOption.iconData, + _replyOption.value.iconData, color: color, ), const SizedBox(width: 4), Text( - _replyOption.title, + _replyOption.value.title, style: TextStyle( height: 1, color: color, @@ -314,7 +423,7 @@ class _CreateDynPanelState extends CommonPublishPageState { ); } - Widget get _buildPubtimeWidget => _publishTime == null + Widget get _buildPubtimeWidget => _publishTime.value == null ? FilledButton.tonal( style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric( @@ -323,7 +432,7 @@ class _CreateDynPanelState extends CommonPublishPageState { ), visualDensity: VisualDensity.compact, ), - onPressed: _isPrivate + onPressed: _isPrivate.value ? null : () { DateTime nowDate = DateTime.now(); @@ -363,15 +472,13 @@ class _CreateDynPanelState extends CommonPublishPageState { } } } - setState(() { - _publishTime = DateTime( - selectedDate.year, - selectedDate.month, - selectedDate.day, - selectedTime.hour, - selectedTime.minute, - ); - }); + _publishTime.value = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); } }); } @@ -388,42 +495,41 @@ class _CreateDynPanelState extends CommonPublishPageState { ), visualDensity: VisualDensity.compact, ), - onPressed: () => setState(() => _publishTime = null), - label: Text(DateFormat('yyyy-MM-dd HH:mm').format(_publishTime!)), + onPressed: () => _publishTime.value = null, + label: + Text(DateFormat('yyyy-MM-dd HH:mm').format(_publishTime.value!)), icon: const Icon(Icons.clear, size: 20), iconAlignment: IconAlignment.end, ); - Widget get _buildToolbar => Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Row( - children: [ - Obx( - () => ToolbarIconButton( - onPressed: () { - selectKeyboard.value = PanelType.emoji == currentPanelType; - updatePanelType( - PanelType.emoji == currentPanelType - ? PanelType.keyboard - : PanelType.emoji, - ); - }, - icon: const Icon(Icons.emoji_emotions, size: 22), - tooltip: '表情', - selected: !selectKeyboard.value, - ), + Widget get _buildToolbar => GestureDetector( + onTap: hidePanel, + behavior: HitTestBehavior.opaque, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Obx( + () => ToolbarIconButton( + onPressed: () { + updatePanelType( + panelType.value == PanelType.emoji + ? PanelType.keyboard + : PanelType.emoji, + ); + }, + icon: const Icon(Icons.emoji_emotions, size: 22), + tooltip: '表情', + selected: panelType.value == PanelType.emoji, ), - ], + ), ), ); - Widget get _buildEditWidget => Form( + Widget _buildEditWidget(ThemeData theme) => Form( autovalidateMode: AutovalidateMode.onUserInteraction, child: Listener( onPointerUp: (event) { if (readOnly.value) { updatePanelType(PanelType.keyboard); - selectKeyboard.value = true; } }, child: Obx( @@ -441,14 +547,16 @@ class _CreateDynPanelState extends CommonPublishPageState { enablePublish.value = false; } }, - decoration: const InputDecoration( + decoration: InputDecoration( hintText: '说点什么吧', - border: OutlineInputBorder( + hintStyle: TextStyle(color: theme.colorScheme.outline), + border: const OutlineInputBorder( borderSide: BorderSide.none, gapPadding: 0, ), contentPadding: EdgeInsets.zero, ), + inputFormatters: [LengthLimitingTextInputFormatter(1000)], ), ), ), @@ -461,15 +569,17 @@ class _CreateDynPanelState extends CommonPublishPageState { Future onCustomPublish( {required String message, List? pictures}) async { SmartDialog.showLoading(msg: '正在发布'); - dynamic result = await MsgHttp.createDynamic( + var result = await DynamicsHttp.createDynamic( mid: Accounts.main.mid, rawText: editController.text, pics: pictures, - publishTime: _publishTime != null - ? _publishTime!.millisecondsSinceEpoch ~/ 1000 + publishTime: _publishTime.value != null + ? _publishTime.value!.millisecondsSinceEpoch ~/ 1000 : null, - replyOption: _replyOption, - privatePub: _isPrivate ? 1 : null, + replyOption: _replyOption.value, + privatePub: _isPrivate.value ? 1 : null, + title: _titleEditCtr.text, + topic: topic.value, ); SmartDialog.dismiss(); if (result['status']) { @@ -485,4 +595,28 @@ class _CreateDynPanelState extends CommonPublishPageState { debugPrint('failed to publish: ${result['msg']}'); } } + + Future _onSelectTopic() async { + TopicPubSearchItem? res = await showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + constraints: BoxConstraints( + maxWidth: min(600, context.mediaQueryShortestSide), + ), + builder: (context) => DraggableScrollableSheet( + expand: false, + snap: true, + minChildSize: 0, + maxChildSize: 1, + initialChildSize: 0.65, + snapSizes: [0.65], + builder: (context, scrollController) => + SelectTopicPanel(scrollController: scrollController), + ), + ); + if (res != null) { + topic.value = Pair(first: res.id!, second: res.name!); + } + } } diff --git a/lib/pages/dynamics_repost/view.dart b/lib/pages/dynamics_repost/view.dart index 72e1f42d..147bffe7 100644 --- a/lib/pages/dynamics_repost/view.dart +++ b/lib/pages/dynamics_repost/view.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/common/widgets/button/toolbar_icon_button.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; -import 'package:PiliPlus/http/msg.dart'; +import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/models/common/publish_panel_type.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/common/common_publish_page.dart'; @@ -10,6 +10,7 @@ import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -46,6 +47,9 @@ class RepostPanel extends CommonPublishPage { class _RepostPanelState extends CommonPublishPageState { late bool _isMax = widget.isMax ?? false; + bool? _isExpanded; + + late final _key = GlobalKey(); late final _pic = widget.pic ?? widget.item?.modules.moduleDynamic?.major?.archive?.cover ?? @@ -72,105 +76,122 @@ class _RepostPanelState extends CommonPublishPageState { @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - return AnimatedSize( - alignment: Alignment.topCenter, - curve: Curves.ease, - duration: const Duration(milliseconds: 300), - child: Column( - mainAxisSize: _isMax ? MainAxisSize.max : MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: _isMax ? 16 : 10), - _buildAppBar(theme), - if (_isMax) ...[ - Expanded(child: _buildEditPanel(theme)), - _buildToolbar, - buildPanelContainer(Colors.transparent), - ] else ...[ - _buildEditPanel(theme), - ..._biuldDismiss(theme), + + Widget page([ScrollController? scrollController]) => Column( + key: _isMax ? _key : null, + mainAxisSize: _isMax ? MainAxisSize.max : MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _isMax ? const SizedBox(height: 16) : const SizedBox(height: 10), + _buildAppBar(theme), + if (_isMax) ...[ + Expanded( + child: ListView( + padding: EdgeInsets.zero, + controller: scrollController, + physics: const ClampingScrollPhysics(), + children: _buildEditPanel(theme), + ), + ), + _buildToolbar, + buildPanelContainer(Colors.transparent), + ] else ...[ + ..._buildEditPanel(theme), + ..._biuldDismiss(theme), + ], ], - ], - ), - ); + ); + + Widget child() => _isMax + ? DraggableScrollableSheet( + snap: true, + expand: false, + initialChildSize: 1, + minChildSize: 0, + maxChildSize: 1, + snapSizes: const [1], + builder: (context, scrollController) => page(scrollController), + ) + : page(); + + return _isExpanded == true + ? child() + : AnimatedSize( + alignment: Alignment.topCenter, + curve: Curves.ease, + onEnd: () => _isExpanded = true, + duration: const Duration(milliseconds: 300), + child: child(), + ); } - Widget _buildEditPanel(ThemeData theme) => Column( - mainAxisSize: _isMax ? MainAxisSize.max : MainAxisSize.min, - children: [ - Flexible( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Container( - width: double.infinity, - decoration: _isMax.not - ? BoxDecoration( - border: Border( - left: BorderSide( - width: 2, - color: theme.colorScheme.primary, - ), - ), - ) - : null, - child: _isMax.not - ? _buildEditPlaceHolder(theme) - : _buildEditWidget, - ), - ), - ), - const SizedBox(height: 10), - _buildRefWidget(theme), - ], - ); - - Widget _buildRefWidget(ThemeData theme) => Container( - padding: const EdgeInsets.all(10), - margin: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration( - color: theme.colorScheme.surfaceContainerHigh == - theme.colorScheme.surface - ? theme.colorScheme.onInverseSurface - : theme.colorScheme.surfaceContainerHighest, - borderRadius: const BorderRadius.all(Radius.circular(12)), - ), - child: Row( - children: [ - if (_pic != null) ...[ - NetworkImgLayer( - radius: 6, - width: 40, - height: 40, - src: _pic, - ), - const SizedBox(width: 10), - ], - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_uname?.isNotEmpty == true) - Text( - '@$_uname', - style: TextStyle( + List _buildEditPanel(ThemeData theme) => [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: _isMax + ? _buildEditWidget(theme) + : DecoratedBox( + decoration: BoxDecoration( + border: Border( + left: BorderSide( + width: 2, color: theme.colorScheme.primary, - fontSize: 13, ), ), - Text( - _text, - maxLines: 2, - overflow: TextOverflow.ellipsis, ), - ], + child: _buildEditPlaceHolder(theme), + ), + ), + const SizedBox(height: 10), + _buildRefWidget(theme), + ]; + + Widget _buildRefWidget(ThemeData theme) => Card( + margin: const EdgeInsets.symmetric(horizontal: 16), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: Padding( + padding: const EdgeInsets.all(10), + child: Row( + children: [ + if (_pic != null) ...[ + NetworkImgLayer( + radius: 6, + width: 40, + height: 40, + src: _pic, + ), + const SizedBox(width: 10), + ], + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_uname?.isNotEmpty == true) + Text( + '@$_uname', + style: TextStyle( + color: theme.colorScheme.primary, + fontSize: 13, + ), + ), + Text( + _text, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], + ), ), - ), - ], + ], + ), ), ); Widget _buildEditPlaceHolder(ThemeData theme) => GestureDetector( + behavior: HitTestBehavior.opaque, onTap: () async { setState(() => _isMax = true); await Future.delayed(const Duration(milliseconds: 300)); @@ -178,23 +199,25 @@ class _RepostPanelState extends CommonPublishPageState { focusNode.requestFocus(); } }, - child: Text( - '说点什么吧', - style: TextStyle( - height: 1.75, - fontSize: 15, - color: theme.colorScheme.outline, + child: SizedBox( + width: double.infinity, + child: Text( + '说点什么吧', + style: TextStyle( + height: 1.75, + fontSize: 15, + color: theme.colorScheme.outline, + ), ), ), ); - Widget get _buildEditWidget => Form( + Widget _buildEditWidget(ThemeData theme) => Form( autovalidateMode: AutovalidateMode.onUserInteraction, child: Listener( onPointerUp: (event) { if (readOnly.value) { updatePanelType(PanelType.keyboard); - selectKeyboard.value = true; } }, child: Obx( @@ -204,14 +227,16 @@ class _RepostPanelState extends CommonPublishPageState { maxLines: null, focusNode: focusNode, readOnly: readOnly.value, - decoration: const InputDecoration( + decoration: InputDecoration( hintText: '说点什么吧', - border: OutlineInputBorder( + hintStyle: TextStyle(color: theme.colorScheme.outline), + border: const OutlineInputBorder( borderSide: BorderSide.none, gapPadding: 0, ), - contentPadding: EdgeInsets.symmetric(vertical: 10), + contentPadding: const EdgeInsets.symmetric(vertical: 10), ), + inputFormatters: [LengthLimitingTextInputFormatter(1000)], ), ), ), @@ -294,26 +319,25 @@ class _RepostPanelState extends CommonPublishPageState { ), ); - Widget get _buildToolbar => Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Row( - children: [ - Obx( - () => ToolbarIconButton( - onPressed: () { - selectKeyboard.value = PanelType.emoji == currentPanelType; - updatePanelType( - PanelType.emoji == currentPanelType - ? PanelType.keyboard - : PanelType.emoji, - ); - }, - icon: const Icon(Icons.emoji_emotions, size: 22), - tooltip: '表情', - selected: !selectKeyboard.value, - ), + Widget get _buildToolbar => GestureDetector( + onTap: hidePanel, + behavior: HitTestBehavior.opaque, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Obx( + () => ToolbarIconButton( + onPressed: () { + updatePanelType( + panelType.value == PanelType.emoji + ? PanelType.keyboard + : PanelType.emoji, + ); + }, + icon: const Icon(Icons.emoji_emotions, size: 22), + tooltip: '表情', + selected: panelType.value == PanelType.emoji, ), - ], + ), ), ); @@ -381,7 +405,7 @@ class _RepostPanelState extends CommonPublishPageState { @override Future onCustomPublish( {required String message, List? pictures}) async { - dynamic result = await MsgHttp.createDynamic( + var result = await DynamicsHttp.createDynamic( mid: Accounts.main.mid, dynIdStr: widget.item?.idStr ?? widget.dynIdStr, rid: widget.rid, diff --git a/lib/pages/dynamics_select_topic/controller.dart b/lib/pages/dynamics_select_topic/controller.dart new file mode 100644 index 00000000..c7e67d8c --- /dev/null +++ b/lib/pages/dynamics_select_topic/controller.dart @@ -0,0 +1,38 @@ +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/http/search.dart'; +import 'package:PiliPlus/models/topic_pub_search/data.dart'; +import 'package:PiliPlus/models/topic_pub_search/topic_item.dart'; +import 'package:PiliPlus/pages/common/common_list_controller.dart'; +import 'package:flutter/widgets.dart'; +import 'package:get/get.dart'; + +class SelectTopicController + extends CommonListController { + final controller = TextEditingController(); + + final RxBool enableClear = false.obs; + + @override + void onInit() { + super.onInit(); + queryData(); + } + + @override + List? getDataList(TopicPubSearchData response) { + return response.topicItems; + } + + @override + Future> customGetData() => + SearchHttp.topicPubSearch( + keywords: controller.text, + pageNum: page, + ); + + @override + void onClose() { + controller.dispose(); + super.onClose(); + } +} diff --git a/lib/pages/dynamics_select_topic/view.dart b/lib/pages/dynamics_select_topic/view.dart new file mode 100644 index 00000000..b9d7cab3 --- /dev/null +++ b/lib/pages/dynamics_select_topic/view.dart @@ -0,0 +1,200 @@ +import 'package:PiliPlus/common/widgets/custom_icon.dart'; +import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart'; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/models/topic_pub_search/topic_item.dart'; +import 'package:PiliPlus/pages/dynamics_select_topic/controller.dart'; +import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class SelectTopicPanel extends StatefulWidget { + const SelectTopicPanel({ + super.key, + this.scrollController, + }); + + final ScrollController? scrollController; + + @override + State createState() => _SelectTopicPanelState(); +} + +class _SelectTopicPanelState extends State { + final _controller = Get.put(SelectTopicController()); + + @override + void initState() { + super.initState(); + if (_controller.loadingState.value is Error) { + _controller.onReload(); + } + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Column( + children: [ + InkWell( + onTap: Get.back, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(18), + topRight: Radius.circular(18), + ), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: theme.colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3)), + ), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 5), + child: TextField( + controller: _controller.controller, + onChanged: (value) { + EasyThrottle.throttle( + 'topicPubSearch', + const Duration(milliseconds: 300), + () => _controller + ..enableClear.value = value.isNotEmpty + ..onRefresh() + .whenComplete(() => widget.scrollController?.jumpToTop()), + ); + }, + decoration: InputDecoration( + border: const OutlineInputBorder( + gapPadding: 0, + borderSide: BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(25), + ), + ), + isDense: true, + filled: true, + fillColor: theme.colorScheme.onInverseSurface, + hintText: '搜索话题', + hintStyle: const TextStyle(fontSize: 14), + prefixIcon: const Padding( + padding: EdgeInsets.only(left: 12, right: 4), + child: Icon(Icons.search, size: 20), + ), + prefixIconConstraints: + const BoxConstraints(minHeight: 0, minWidth: 0), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + suffixIcon: Obx( + () => _controller.enableClear.value + ? Padding( + padding: const EdgeInsets.only(right: 12), + child: GestureDetector( + child: Container( + padding: const EdgeInsetsDirectional.all(2), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: theme.colorScheme.secondaryContainer, + ), + child: Icon( + Icons.clear, + size: 16, + color: theme.colorScheme.onSecondaryContainer, + ), + ), + onTap: () => _controller + ..enableClear.value = false + ..controller.clear() + ..onRefresh().whenComplete( + () => widget.scrollController?.jumpToTop()), + ), + ) + : const SizedBox.shrink(), + ), + suffixIconConstraints: + const BoxConstraints(minHeight: 0, minWidth: 0), + ), + ), + ), + Expanded( + child: Material( + color: Colors.transparent, + child: Obx(() => _buildBody(theme, _controller.loadingState.value)), + ), + ), + ], + ); + } + + Widget _buildBody( + ThemeData theme, LoadingState?> loadingState) { + return switch (loadingState) { + Loading() => loadingWidget, + Success?>(:var response) => + response?.isNotEmpty == true + ? ListView.builder( + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + + MediaQuery.viewInsetsOf(context).bottom + + 80, + ), + controller: widget.scrollController, + itemBuilder: (context, index) { + if (index == response.length - 1) { + _controller.onLoadMore(); + } + final item = response[index]; + return ListTile( + dense: true, + onTap: () => Get.back(result: item), + title: Text.rich( + TextSpan( + children: [ + const WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: EdgeInsets.only(right: 5), + child: Icon( + CustomIcon.topic_tag, + size: 18, + ), + ), + ), + TextSpan( + text: item.name, + style: const TextStyle(fontSize: 14), + ), + ], + ), + ), + subtitle: Padding( + padding: const EdgeInsets.only(left: 23), + child: Text( + '${Utils.numFormat(item.view)}浏览 · ${Utils.numFormat(item.discuss)}讨论', + style: TextStyle(color: theme.colorScheme.outline), + ), + ), + ); + }, + itemCount: response!.length, + ) + : scrollErrorWidget( + controller: widget.scrollController, + onReload: _controller.onReload, + ), + Error(:var errMsg) => scrollErrorWidget( + errMsg: errMsg, + controller: widget.scrollController, + onReload: _controller.onReload, + ), + }; + } +} diff --git a/lib/pages/fav_create/view.dart b/lib/pages/fav_create/view.dart index 16087472..9c787d4b 100644 --- a/lib/pages/fav_create/view.dart +++ b/lib/pages/fav_create/view.dart @@ -5,7 +5,7 @@ import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:image_cropper/image_cropper.dart'; diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart index 69981d2b..f04887a6 100644 --- a/lib/pages/follow/view.dart +++ b/lib/pages/follow/view.dart @@ -7,7 +7,7 @@ import 'package:PiliPlus/pages/follow/child_view.dart'; import 'package:PiliPlus/pages/follow/controller.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:get/get.dart'; class FollowPage extends StatefulWidget { diff --git a/lib/pages/live_room/send_danmaku/view.dart b/lib/pages/live_room/send_danmaku/view.dart index 3d3157a5..465f23c4 100644 --- a/lib/pages/live_room/send_danmaku/view.dart +++ b/lib/pages/live_room/send_danmaku/view.dart @@ -8,7 +8,7 @@ import 'package:PiliPlus/pages/live_emote/controller.dart'; import 'package:PiliPlus/pages/live_emote/view.dart'; import 'package:PiliPlus/pages/live_room/controller.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart' hide MultipartFile; @@ -35,7 +35,6 @@ class _ReplyPageState extends CommonPublishPageState { void initState() { super.initState(); if (widget.fromEmote) { - selectKeyboard.value = false; updatePanelType(PanelType.emoji); } } @@ -100,7 +99,6 @@ class _ReplyPageState extends CommonPublishPageState { onPointerUp: (event) { if (readOnly.value) { updatePanelType(PanelType.keyboard); - selectKeyboard.value = true; } }, child: Obx( @@ -146,13 +144,12 @@ class _ReplyPageState extends CommonPublishPageState { () => ToolbarIconButton( tooltip: '输入', onPressed: () { - if (!selectKeyboard.value) { - selectKeyboard.value = true; + if (panelType.value != PanelType.keyboard) { updatePanelType(PanelType.keyboard); } }, icon: const Icon(Icons.keyboard, size: 22), - selected: selectKeyboard.value, + selected: panelType.value == PanelType.keyboard, ), ), const SizedBox(width: 10), @@ -160,13 +157,12 @@ class _ReplyPageState extends CommonPublishPageState { () => ToolbarIconButton( tooltip: '表情', onPressed: () { - if (selectKeyboard.value) { - selectKeyboard.value = false; + if (panelType.value != PanelType.emoji) { updatePanelType(PanelType.emoji); } }, icon: const Icon(Icons.emoji_emotions, size: 22), - selected: !selectKeyboard.value, + selected: panelType.value == PanelType.emoji, ), ), const Spacer(), diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index f57a076e..24a4a797 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -22,7 +22,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show MethodChannel; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:screen_brightness/screen_brightness.dart'; diff --git a/lib/pages/member_profile/view.dart b/lib/pages/member_profile/view.dart index 9b61755c..080d27c8 100644 --- a/lib/pages/member_profile/view.dart +++ b/lib/pages/member_profile/view.dart @@ -12,7 +12,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart' hide FormData, MultipartFile; import 'package:image_cropper/image_cropper.dart'; diff --git a/lib/pages/save_panel/view.dart b/lib/pages/save_panel/view.dart index ef370f9b..b1cfb892 100644 --- a/lib/pages/save_panel/view.dart +++ b/lib/pages/save_panel/view.dart @@ -1,4 +1,3 @@ -import 'dart:math'; import 'dart:typed_data'; import 'dart:ui'; @@ -291,7 +290,7 @@ class _SavePanelState extends State { child: GestureDetector( onTap: () {}, child: Container( - width: min(Get.width, Get.height), + width: context.mediaQueryShortestSide, margin: const EdgeInsets.symmetric(horizontal: 12), child: RepaintBoundary( key: boundaryKey, diff --git a/lib/pages/search_panel/article/controller.dart b/lib/pages/search_panel/article/controller.dart index 308d0a57..5cded803 100644 --- a/lib/pages/search_panel/article/controller.dart +++ b/lib/pages/search_panel/article/controller.dart @@ -67,7 +67,7 @@ class SearchArticleController isScrollControlled: true, clipBehavior: Clip.hardEdge, constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (context) { final theme = Theme.of(context); diff --git a/lib/pages/search_panel/user/controller.dart b/lib/pages/search_panel/user/controller.dart index 7038c9f9..6bcbf941 100644 --- a/lib/pages/search_panel/user/controller.dart +++ b/lib/pages/search_panel/user/controller.dart @@ -39,7 +39,7 @@ class SearchUserController isScrollControlled: true, clipBehavior: Clip.hardEdge, constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (context) { final theme = Theme.of(context); diff --git a/lib/pages/search_panel/video/controller.dart b/lib/pages/search_panel/video/controller.dart index afd36d75..12e5ee30 100644 --- a/lib/pages/search_panel/video/controller.dart +++ b/lib/pages/search_panel/video/controller.dart @@ -147,7 +147,7 @@ class SearchVideoController isScrollControlled: true, clipBehavior: Clip.hardEdge, constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (context) => StatefulBuilder( builder: (context, setState) { diff --git a/lib/pages/search_trending/view.dart b/lib/pages/search_trending/view.dart index 261e96a0..04d17ae2 100644 --- a/lib/pages/search_trending/view.dart +++ b/lib/pages/search_trending/view.dart @@ -9,7 +9,7 @@ import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show SystemUiOverlayStyle; import 'package:get/get.dart'; class SearchTrendingPage extends StatefulWidget { diff --git a/lib/pages/setting/pages/display_mode.dart b/lib/pages/setting/pages/display_mode.dart index 35fdb869..d17edca3 100644 --- a/lib/pages/setting/pages/display_mode.dart +++ b/lib/pages/setting/pages/display_mode.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show PlatformException; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hive/hive.dart'; diff --git a/lib/pages/setting/pages/play_speed_set.dart b/lib/pages/setting/pages/play_speed_set.dart index 2213edad..4e1e9684 100644 --- a/lib/pages/setting/pages/play_speed_set.dart +++ b/lib/pages/setting/pages/play_speed_set.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show FilteringTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; @@ -138,7 +138,7 @@ class _PlaySpeedPageState extends State { isScrollControlled: true, clipBehavior: Clip.hardEdge, constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (context) { return Column( diff --git a/lib/pages/setting/slide_color_picker.dart b/lib/pages/setting/slide_color_picker.dart index 42557442..5a73fe95 100644 --- a/lib/pages/setting/slide_color_picker.dart +++ b/lib/pages/setting/slide_color_picker.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' + show LengthLimitingTextInputFormatter, FilteringTextInputFormatter; import 'package:get/get.dart'; class SlideColorPicker extends StatefulWidget { diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart index a76c6729..33e7cf74 100644 --- a/lib/pages/setting/widgets/model.dart +++ b/lib/pages/setting/widgets/model.dart @@ -53,7 +53,7 @@ import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:auto_orientation/auto_orientation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show FilteringTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/share/view.dart b/lib/pages/share/view.dart index f21080b5..9751dc84 100644 --- a/lib/pages/share/view.dart +++ b/lib/pages/share/view.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/pages/contact/view.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -239,6 +240,7 @@ class _SharePanelState extends State { const EdgeInsets.symmetric(horizontal: 16, vertical: 8), fillColor: theme.colorScheme.onInverseSurface, ), + inputFormatters: [LengthLimitingTextInputFormatter(100)], ), ), const SizedBox(width: 12), diff --git a/lib/pages/sponsor_block/view.dart b/lib/pages/sponsor_block/view.dart index f6a61c77..115d70d8 100644 --- a/lib/pages/sponsor_block/view.dart +++ b/lib/pages/sponsor_block/view.dart @@ -10,7 +10,7 @@ import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show FilteringTextInputFormatter; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index 04450dd9..dd76a103 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -127,9 +127,11 @@ class VideoDetailController extends GetxController vsync: this, duration: const Duration(milliseconds: 200), ); - late final double minVideoHeight = min(Get.height, Get.width) * 9 / 16; - late final double maxVideoHeight = - max(max(Get.height, Get.width) * 0.65, min(Get.height, Get.width)); + late final double minVideoHeight = Get.mediaQuery.size.shortestSide * 9 / 16; + late final double maxVideoHeight = max( + Get.mediaQuery.size.longestSide * 0.65, + Get.mediaQuery.size.shortestSide, + ); late double videoHeight = minVideoHeight; void animToTop() { diff --git a/lib/pages/video/introduction/pgc/view.dart b/lib/pages/video/introduction/pgc/view.dart index 2bb98003..ddaebff6 100644 --- a/lib/pages/video/introduction/pgc/view.dart +++ b/lib/pages/video/introduction/pgc/view.dart @@ -15,7 +15,7 @@ import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_row_item.da import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show HapticFeedback; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index b5198aba..53924ab4 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -27,7 +27,7 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show HapticFeedback; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/video/introduction/ugc/widgets/action_item.dart b/lib/pages/video/introduction/ugc/widgets/action_item.dart index 06eeca2b..aac11262 100644 --- a/lib/pages/video/introduction/ugc/widgets/action_item.dart +++ b/lib/pages/video/introduction/ugc/widgets/action_item.dart @@ -4,7 +4,7 @@ import 'dart:math'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show HapticFeedback; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; class ActionItem extends StatefulWidget { diff --git a/lib/pages/video/post_panel/view.dart b/lib/pages/video/post_panel/view.dart index 720d26d3..4e6ec0ba 100644 --- a/lib/pages/video/post_panel/view.dart +++ b/lib/pages/video/post_panel/view.dart @@ -17,7 +17,7 @@ import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show FilteringTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -485,9 +485,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { initV = value; }, inputFormatters: [ - FilteringTextInputFormatter.allow( - RegExp(r'[\d:.]+'), - ), + FilteringTextInputFormatter.allow(RegExp(r'[\d:.]+')), ], ), actions: [ diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index aea8b3f3..d34b1f42 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -86,7 +86,7 @@ class ReplyItemGrpc extends StatelessWidget { useSafeArea: true, isScrollControlled: true, constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (context) { return morePanel( @@ -446,7 +446,7 @@ class ReplyItemGrpc extends StatelessWidget { useSafeArea: true, isScrollControlled: true, constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (context) { return morePanel( diff --git a/lib/pages/video/reply_new/view.dart b/lib/pages/video/reply_new/view.dart index b98abb82..fe0ae474 100644 --- a/lib/pages/video/reply_new/view.dart +++ b/lib/pages/video/reply_new/view.dart @@ -125,7 +125,6 @@ class _ReplyPageState extends CommonPublishPageState { onPointerUp: (event) { if (readOnly.value) { updatePanelType(PanelType.keyboard); - selectKeyboard.value = true; } }, child: Obx( @@ -170,13 +169,12 @@ class _ReplyPageState extends CommonPublishPageState { () => ToolbarIconButton( tooltip: '输入', onPressed: () { - if (!selectKeyboard.value) { - selectKeyboard.value = true; + if (panelType.value != PanelType.keyboard) { updatePanelType(PanelType.keyboard); } }, icon: const Icon(Icons.keyboard, size: 22), - selected: selectKeyboard.value, + selected: panelType.value == PanelType.keyboard, ), ), const SizedBox(width: 10), @@ -184,13 +182,12 @@ class _ReplyPageState extends CommonPublishPageState { () => ToolbarIconButton( tooltip: '表情', onPressed: () { - if (selectKeyboard.value) { - selectKeyboard.value = false; + if (panelType.value != PanelType.emoji) { updatePanelType(PanelType.emoji); } }, icon: const Icon(Icons.emoji_emotions, size: 22), - selected: !selectKeyboard.value, + selected: panelType.value == PanelType.emoji, ), ), if (widget.root == 0) ...[ diff --git a/lib/pages/video/send_danmaku/view.dart b/lib/pages/video/send_danmaku/view.dart index 0a259e58..6603034c 100644 --- a/lib/pages/video/send_danmaku/view.dart +++ b/lib/pages/video/send_danmaku/view.dart @@ -10,7 +10,7 @@ import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:canvas_danmaku/models/danmaku_content_item.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -342,18 +342,14 @@ class _SendDanmakuPanelState extends CommonPublishPageState { context: context, tooltip: '弹幕样式', onPressed: () { - if (selectKeyboard.value) { - selectKeyboard.value = false; - updatePanelType(PanelType.emoji); - } else { - selectKeyboard.value = true; - updatePanelType(PanelType.keyboard); - } + updatePanelType(panelType.value == PanelType.keyboard + ? PanelType.emoji + : PanelType.keyboard); }, bgColor: Colors.transparent, iconSize: 24, icon: Icons.text_format, - iconColor: selectKeyboard.value.not + iconColor: panelType.value == PanelType.emoji ? themeData.colorScheme.primary : themeData.colorScheme.onSurfaceVariant, ), @@ -366,7 +362,6 @@ class _SendDanmakuPanelState extends CommonPublishPageState { onPointerUp: (event) { if (readOnly.value) { updatePanelType(PanelType.keyboard); - selectKeyboard.value = true; } }, child: Obx( diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index 5b30e177..925d6c51 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -55,7 +55,7 @@ import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show SystemUiOverlayStyle; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 6f3d643b..a9e3b4c2 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -33,7 +33,7 @@ import 'package:dio/dio.dart'; import 'package:document_file_save_plus/document_file_save_plus_platform_interface.dart'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show HapticFeedback; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/whisper_block/view.dart b/lib/pages/whisper_block/view.dart index ccea8a02..94e947de 100644 --- a/lib/pages/whisper_block/view.dart +++ b/lib/pages/whisper_block/view.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/pages/search/widgets/search_text.dart'; import 'package:PiliPlus/pages/whisper_block/controller.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_svg/svg.dart'; import 'package:get/get.dart'; @@ -195,6 +196,7 @@ class _WhisperBlockPageState extends State { fillColor: theme.colorScheme.onInverseSurface, ), onChanged: (value) => keyword = value, + inputFormatters: [LengthLimitingTextInputFormatter(20)], ), const SizedBox(height: 12), FilledButton.tonal( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index e21076b6..804b637a 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -17,6 +17,7 @@ import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; @@ -227,7 +228,7 @@ class _WhisperDetailPageState children: [ IconButton( onPressed: () => updatePanelType( - PanelType.emoji == currentPanelType + panelType.value == PanelType.emoji ? PanelType.keyboard : PanelType.emoji, ), @@ -269,6 +270,7 @@ class _WhisperDetailPageState ), contentPadding: const EdgeInsets.all(10), ), + inputFormatters: [LengthLimitingTextInputFormatter(500)], ), ), ), diff --git a/lib/utils/feed_back.dart b/lib/utils/feed_back.dart index 4bfc898b..d726633a 100644 --- a/lib/utils/feed_back.dart +++ b/lib/utils/feed_back.dart @@ -1,5 +1,5 @@ import 'package:PiliPlus/utils/storage.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show HapticFeedback; bool enableFeedback = GStorage.feedBackEnable; void feedBack() { diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index 650c54d3..0a4d22e6 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -24,7 +24,7 @@ import 'package:PiliPlus/utils/url_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show FilteringTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -300,7 +300,7 @@ class PageUtils { isScrollControlled: true, sheetAnimationStyle: const AnimationStyle(curve: Curves.ease), constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (BuildContext context) { return DraggableScrollableSheet( diff --git a/lib/utils/request_utils.dart b/lib/utils/request_utils.dart index 1c070d54..aacb7f18 100644 --- a/lib/utils/request_utils.dart +++ b/lib/utils/request_utils.dart @@ -160,7 +160,7 @@ class RequestUtils { sheetAnimationStyle: const AnimationStyle(curve: Curves.ease), constraints: BoxConstraints( - maxWidth: min(640, min(Get.width, Get.height)), + maxWidth: min(640, context.mediaQueryShortestSide), ), builder: (BuildContext context) { return DraggableScrollableSheet(