opt pub panel

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-05-26 17:11:52 +08:00
parent db3b74e33f
commit 3edac65ae8
49 changed files with 879 additions and 360 deletions

View File

@@ -141,9 +141,7 @@ class _ReasonFieldState extends State<ReasonField> {
border: OutlineInputBorder(),
contentPadding: EdgeInsets.all(10),
),
onChanged: (value) {
widget.onChanged(value);
},
onChanged: widget.onChanged,
validator: widget._validator,
),
);

View File

@@ -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) {

View File

@@ -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';
}

View File

@@ -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<Map<String, dynamic>>? extraContent,
Pair<int, String>? 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,

View File

@@ -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<Map<String, dynamic>>? 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,

View File

@@ -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<LoadingState<TopicPubSearchData>> 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']);
}
}
}

View File

@@ -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<TopicPubSearchItem>? topicItems;
String? requestId;
PageInfo? pageInfo;
TopicPubSearchData({
this.newTopic,
this.hasCreateJurisdiction,
this.topicItems,
this.requestId,
this.pageInfo,
});
factory TopicPubSearchData.fromJson(Map<String, dynamic> json) =>
TopicPubSearchData(
newTopic: json['new_topic'] == null
? null
: NewTopic.fromJson(json['new_topic'] as Map<String, dynamic>),
hasCreateJurisdiction: json['has_create_jurisdiction'] as bool?,
topicItems: (json['topic_items'] as List<dynamic>?)
?.map((e) => TopicPubSearchItem.fromJson(e as Map<String, dynamic>))
.toList(),
requestId: json['request_id'] as String?,
pageInfo: json['page_info'] == null
? null
: PageInfo.fromJson(json['page_info'] as Map<String, dynamic>),
);
}

View File

@@ -0,0 +1,9 @@
class NewTopic {
String? name;
NewTopic({this.name});
factory NewTopic.fromJson(Map<String, dynamic> json) => NewTopic(
name: json['name'] as String?,
);
}

View File

@@ -0,0 +1,11 @@
class PageInfo {
int? offset;
bool? hasMore;
PageInfo({this.offset, this.hasMore});
factory PageInfo.fromJson(Map<String, dynamic> json) => PageInfo(
offset: json['offset'] as int?,
hasMore: json['has_more'] as bool?,
);
}

View File

@@ -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<String, dynamic> 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?,
);
}

View File

@@ -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';

View File

@@ -40,10 +40,9 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
late final controller = ChatBottomPanelContainerController<PanelType>();
late final editController = TextEditingController(text: widget.initialValue);
PanelType currentPanelType = PanelType.none;
Rx<PanelType> 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<String> pathList = <String>[].obs;
@@ -83,7 +82,9 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
@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<T extends CommonPublishPage>
}
},
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;

View File

@@ -44,7 +44,16 @@ class _DynamicsPageState extends State<DynamicsPage>
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),
),
);
}
},

View File

@@ -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);

View File

@@ -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<CreateDynPanel> createState() => _CreateDynPanelState();
}
class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
bool _isPrivate = false;
DateTime? _publishTime;
ReplyOptionType _replyOption = ReplyOptionType.allow;
final RxBool _isPrivate = false.obs;
final Rx<DateTime?> _publishTime = Rx<DateTime?>(null);
final Rx<ReplyOptionType> _replyOption = ReplyOptionType.allow.obs;
final _titleEditCtr = TextEditingController();
Rx<Pair<int, String>?> topic = Rx<Pair<int, String>?>(null);
@override
void dispose() {
_titleEditCtr.dispose();
try {
Get.delete<EmotePanelController>();
Get
..delete<EmotePanelController>()
..delete<SelectTopicController>();
} catch (_) {}
super.dispose();
}
@@ -46,15 +63,115 @@ class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
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(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: _buildEditWidget,
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(
@@ -62,14 +179,14 @@ class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
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<CreateDynPanel> {
),
const SizedBox(height: 10),
_buildImageList(theme),
const SizedBox(height: 2),
],
),
),
@@ -186,7 +302,7 @@ class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
),
visualDensity: VisualDensity.compact,
),
child: Text(_publishTime == null ? '发布' : '定时发布'),
child: Text(_publishTime.value == null ? '发布' : '定时发布'),
),
),
),
@@ -196,20 +312,17 @@ class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
);
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<bool>(
initialValue: _isPrivate.value,
onOpened: controller.keepChatPanel,
onSelected: (value) {
setState(() {
_isPrivate = value;
});
},
onSelected: (value) => _isPrivate.value = value,
itemBuilder: (context) => List.generate(
2,
(index) => PopupMenuItem<bool>(
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<CreateDynPanel> {
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<CreateDynPanel> {
}
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<ReplyOptionType>(
initialValue: _replyOption.value,
onOpened: controller.keepChatPanel,
onSelected: (item) {
setState(() {
_replyOption = item;
});
},
onSelected: (item) => _replyOption.value = item,
itemBuilder: (context) => ReplyOptionType.values
.map(
(item) => PopupMenuItem<ReplyOptionType>(
@@ -291,12 +400,12 @@ class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
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<CreateDynPanel> {
);
}
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<CreateDynPanel> {
),
visualDensity: VisualDensity.compact,
),
onPressed: _isPrivate
onPressed: _isPrivate.value
? null
: () {
DateTime nowDate = DateTime.now();
@@ -363,15 +472,13 @@ class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
}
}
}
setState(() {
_publishTime = DateTime(
_publishTime.value = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
});
}
});
}
@@ -388,42 +495,41 @@ class _CreateDynPanelState extends CommonPublishPageState<CreateDynPanel> {
),
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(
Widget get _buildToolbar => GestureDetector(
onTap: hidePanel,
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Obx(
child: Obx(
() => ToolbarIconButton(
onPressed: () {
selectKeyboard.value = PanelType.emoji == currentPanelType;
updatePanelType(
PanelType.emoji == currentPanelType
panelType.value == PanelType.emoji
? PanelType.keyboard
: PanelType.emoji,
);
},
icon: const Icon(Icons.emoji_emotions, size: 22),
tooltip: '表情',
selected: !selectKeyboard.value,
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<CreateDynPanel> {
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<CreateDynPanel> {
Future<void> 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<CreateDynPanel> {
debugPrint('failed to publish: ${result['msg']}');
}
}
Future<void> _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!);
}
}
}

View File

@@ -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<RepostPanel> {
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,68 +76,83 @@ class _RepostPanelState extends CommonPublishPageState<RepostPanel> {
@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(
Widget page([ScrollController? scrollController]) => Column(
key: _isMax ? _key : null,
mainAxisSize: _isMax ? MainAxisSize.max : MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: _isMax ? 16 : 10),
_isMax ? const SizedBox(height: 16) : const SizedBox(height: 10),
_buildAppBar(theme),
if (_isMax) ...[
Expanded(child: _buildEditPanel(theme)),
Expanded(
child: ListView(
padding: EdgeInsets.zero,
controller: scrollController,
physics: const ClampingScrollPhysics(),
children: _buildEditPanel(theme),
),
),
_buildToolbar,
buildPanelContainer(Colors.transparent),
] else ...[
_buildEditPanel(theme),
..._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(
List<Widget> _buildEditPanel(ThemeData theme) => [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
width: double.infinity,
decoration: _isMax.not
? BoxDecoration(
child: _isMax
? _buildEditWidget(theme)
: DecoratedBox(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 2,
color: theme.colorScheme.primary,
),
),
)
: null,
child: _isMax.not
? _buildEditPlaceHolder(theme)
: _buildEditWidget,
),
child: _buildEditPlaceHolder(theme),
),
),
const SizedBox(height: 10),
_buildRefWidget(theme),
],
);
];
Widget _buildRefWidget(ThemeData theme) => Container(
padding: const EdgeInsets.all(10),
Widget _buildRefWidget(ThemeData theme) => Card(
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)),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: Padding(
padding: const EdgeInsets.all(10),
child: Row(
children: [
if (_pic != null) ...[
@@ -168,9 +187,11 @@ class _RepostPanelState extends CommonPublishPageState<RepostPanel> {
),
],
),
),
);
Widget _buildEditPlaceHolder(ThemeData theme) => GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
setState(() => _isMax = true);
await Future.delayed(const Duration(milliseconds: 300));
@@ -178,6 +199,8 @@ class _RepostPanelState extends CommonPublishPageState<RepostPanel> {
focusNode.requestFocus();
}
},
child: SizedBox(
width: double.infinity,
child: Text(
'说点什么吧',
style: TextStyle(
@@ -186,15 +209,15 @@ class _RepostPanelState extends CommonPublishPageState<RepostPanel> {
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<RepostPanel> {
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<RepostPanel> {
),
);
Widget get _buildToolbar => Padding(
Widget get _buildToolbar => GestureDetector(
onTap: hidePanel,
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Obx(
child: Obx(
() => ToolbarIconButton(
onPressed: () {
selectKeyboard.value = PanelType.emoji == currentPanelType;
updatePanelType(
PanelType.emoji == currentPanelType
panelType.value == PanelType.emoji
? PanelType.keyboard
: PanelType.emoji,
);
},
icon: const Icon(Icons.emoji_emotions, size: 22),
tooltip: '表情',
selected: !selectKeyboard.value,
selected: panelType.value == PanelType.emoji,
),
),
],
),
);
@@ -381,7 +405,7 @@ class _RepostPanelState extends CommonPublishPageState<RepostPanel> {
@override
Future<void> 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,

View File

@@ -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<TopicPubSearchData, TopicPubSearchItem> {
final controller = TextEditingController();
final RxBool enableClear = false.obs;
@override
void onInit() {
super.onInit();
queryData();
}
@override
List<TopicPubSearchItem>? getDataList(TopicPubSearchData response) {
return response.topicItems;
}
@override
Future<LoadingState<TopicPubSearchData>> customGetData() =>
SearchHttp.topicPubSearch(
keywords: controller.text,
pageNum: page,
);
@override
void onClose() {
controller.dispose();
super.onClose();
}
}

View File

@@ -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<SelectTopicPanel> createState() => _SelectTopicPanelState();
}
class _SelectTopicPanelState extends State<SelectTopicPanel> {
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<List<TopicPubSearchItem>?> loadingState) {
return switch (loadingState) {
Loading() => loadingWidget,
Success<List<TopicPubSearchItem>?>(: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,
),
};
}
}

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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<LiveSendDmPanel> {
void initState() {
super.initState();
if (widget.fromEmote) {
selectKeyboard.value = false;
updatePanelType(PanelType.emoji);
}
}
@@ -100,7 +99,6 @@ class _ReplyPageState extends CommonPublishPageState<LiveSendDmPanel> {
onPointerUp: (event) {
if (readOnly.value) {
updatePanelType(PanelType.keyboard);
selectKeyboard.value = true;
}
},
child: Obx(
@@ -146,13 +144,12 @@ class _ReplyPageState extends CommonPublishPageState<LiveSendDmPanel> {
() => 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<LiveSendDmPanel> {
() => 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(),

View File

@@ -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';

View File

@@ -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';

View File

@@ -1,4 +1,3 @@
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';
@@ -291,7 +290,7 @@ class _SavePanelState extends State<SavePanel> {
child: GestureDetector(
onTap: () {},
child: Container(
width: min(Get.width, Get.height),
width: context.mediaQueryShortestSide,
margin: const EdgeInsets.symmetric(horizontal: 12),
child: RepaintBoundary(
key: boundaryKey,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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';

View File

@@ -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<PlaySpeedPage> {
isScrollControlled: true,
clipBehavior: Clip.hardEdge,
constraints: BoxConstraints(
maxWidth: min(640, min(Get.width, Get.height)),
maxWidth: min(640, context.mediaQueryShortestSide),
),
builder: (context) {
return Column(

View File

@@ -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 {

View File

@@ -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';

View File

@@ -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<SharePanel> {
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
fillColor: theme.colorScheme.onInverseSurface,
),
inputFormatters: [LengthLimitingTextInputFormatter(100)],
),
),
const SizedBox(width: 12),

View File

@@ -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';

View File

@@ -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() {

View File

@@ -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';

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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<PostPanel> {
initV = value;
},
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp(r'[\d:.]+'),
),
FilteringTextInputFormatter.allow(RegExp(r'[\d:.]+')),
],
),
actions: [

View File

@@ -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(

View File

@@ -125,7 +125,6 @@ class _ReplyPageState extends CommonPublishPageState<ReplyPage> {
onPointerUp: (event) {
if (readOnly.value) {
updatePanelType(PanelType.keyboard);
selectKeyboard.value = true;
}
},
child: Obx(
@@ -170,13 +169,12 @@ class _ReplyPageState extends CommonPublishPageState<ReplyPage> {
() => 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<ReplyPage> {
() => 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) ...[

View File

@@ -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<SendDanmakuPanel> {
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<SendDanmakuPanel> {
onPointerUp: (event) {
if (readOnly.value) {
updatePanelType(PanelType.keyboard);
selectKeyboard.value = true;
}
},
child: Obx(

View File

@@ -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';

View File

@@ -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';

View File

@@ -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<WhisperBlockPage> {
fillColor: theme.colorScheme.onInverseSurface,
),
onChanged: (value) => keyword = value,
inputFormatters: [LengthLimitingTextInputFormatter(20)],
),
const SizedBox(height: 12),
FilledButton.tonal(

View File

@@ -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)],
),
),
),

View File

@@ -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() {

View File

@@ -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(

View File

@@ -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(