mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-15 22:56:15 +08:00
opt pub panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -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,30 +63,130 @@ 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: 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<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(
|
||||
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<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(
|
||||
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<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!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user