mod: split some pages

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-01-24 17:50:43 +08:00
parent f3b1c416b9
commit be20b1bd73
18 changed files with 1472 additions and 1461 deletions

View File

@@ -0,0 +1,527 @@
import 'dart:io';
import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/pages/dynamics/view.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
class CreateDynPanel extends StatefulWidget {
const CreateDynPanel({super.key});
@override
State<CreateDynPanel> createState() => _CreateDynPanelState();
}
class _CreateDynPanelState extends State<CreateDynPanel> {
final _ctr = TextEditingController();
late final _imagePicker = ImagePicker();
late final int _limit = 18;
final RxBool _isEnablePub = false.obs;
late final RxList<String> _pathList = <String>[].obs;
bool _isPrivate = false;
DateTime? _publishTime;
ReplyOption _replyOption = ReplyOption.allow;
@override
void dispose() {
_ctr.dispose();
super.dispose();
}
Future _onCreate() async {
// if (_pathList.isEmpty) {
// dynamic result = await MsgHttp.createTextDynamic(_ctr.text);
// if (result['status']) {
// Get.back();
// SmartDialog.showToast('发布成功');
// } else {
// SmartDialog.showToast(result['msg']);
// }
// } else {
List? pics;
if (_pathList.isNotEmpty) {
pics = [];
for (int i = 0; i < _pathList.length; i++) {
SmartDialog.showLoading(msg: '正在上传图片: ${i + 1}/${_pathList.length}');
dynamic result = await MsgHttp.uploadBfs(
path: _pathList[i],
category: 'daily',
biz: 'new_dyn',
);
if (result['status']) {
int imageSize = await File(_pathList[i]).length();
pics.add({
'img_width': result['data']['image_width'],
'img_height': result['data']['image_height'],
'img_size': imageSize / 1024,
'img_src': result['data']['image_url'],
});
} else {
SmartDialog.dismiss();
SmartDialog.showToast(result['msg']);
return;
}
if (i == _pathList.length - 1) {
SmartDialog.dismiss();
}
}
}
SmartDialog.showLoading(msg: '正在发布');
dynamic result = await MsgHttp.createDynamic(
mid: GStorage.userInfo.get('userInfoCache')?.mid,
rawText: _ctr.text,
pics: pics,
publishTime: _publishTime != null
? _publishTime!.millisecondsSinceEpoch ~/ 1000
: null,
replyOption: _replyOption,
privatePub: _isPrivate ? 1 : null,
);
if (result['status']) {
Get.back();
SmartDialog.dismiss();
SmartDialog.showToast('发布成功');
} else {
SmartDialog.dismiss();
SmartDialog.showToast(result['msg']);
}
// }
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: true,
appBar: PreferredSize(
preferredSize: Size.fromHeight(66),
child: Padding(
padding: const EdgeInsets.all(16),
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
bottom: 0,
child: SizedBox(
width: 34,
height: 34,
child: IconButton(
tooltip: '返回',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.resolveWith(
(states) {
return Theme.of(context)
.colorScheme
.secondaryContainer;
},
),
),
onPressed: Get.back,
icon: Icon(
Icons.arrow_back_outlined,
size: 18,
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
),
),
Center(
child: const Text(
'发布动态',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
),
Positioned(
top: 0,
right: 0,
child: Obx(
() => FilledButton.tonal(
onPressed: _isEnablePub.value ? _onCreate : null,
style: FilledButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
child: Text(_publishTime == null ? '发布' : '定时发布'),
),
),
),
],
),
),
),
body: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextField(
controller: _ctr,
minLines: 4,
maxLines: 8,
autofocus: true,
onChanged: (value) {
bool isEmpty = value.trim().isEmpty && _pathList.isEmpty;
if (!isEmpty && !_isEnablePub.value) {
_isEnablePub.value = true;
} else if (isEmpty && _isEnablePub.value) {
_isEnablePub.value = false;
}
},
decoration: const InputDecoration(
hintText: '说点什么吧',
border: OutlineInputBorder(
borderSide: BorderSide.none,
gapPadding: 0,
),
contentPadding: EdgeInsets.zero,
),
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_publishTime == null
? FilledButton.tonal(
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
onPressed: _isPrivate
? null
: () {
DateTime nowDate = DateTime.now();
showDatePicker(
context: context,
initialDate: nowDate,
firstDate: nowDate,
lastDate: DateTime(
nowDate.year,
nowDate.month,
nowDate.day + 7,
),
).then(
(selectedDate) {
if (selectedDate != null &&
context.mounted) {
TimeOfDay nowTime = TimeOfDay.now();
showTimePicker(
context: context,
initialTime: nowTime.replacing(
hour: nowTime.minute + 6 >= 60
? (nowTime.hour + 1) % 24
: nowTime.hour,
minute: (nowTime.minute + 6) % 60,
),
).then((selectedTime) {
if (selectedTime != null) {
if (selectedDate.day ==
nowDate.day) {
if (selectedTime.hour <
nowTime.hour) {
SmartDialog.showToast(
'时间设置错误至少选择6分钟之后');
return;
} else if (selectedTime.hour ==
nowTime.hour) {
if (selectedTime.minute <
nowTime.minute + 6) {
if (selectedDate.day ==
nowDate.day) {
SmartDialog.showToast(
'时间设置错误至少选择6分钟之后');
}
return;
}
}
}
setState(() {
_publishTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
});
}
});
}
},
);
},
child: const Text('定时发布'),
)
: OutlinedButton.icon(
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
onPressed: () {
setState(() {
_publishTime = null;
});
},
label: Text(DateFormat('yyyy-MM-dd HH:mm')
.format(_publishTime!)),
icon: Icon(Icons.clear, size: 20),
iconAlignment: IconAlignment.end,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PopupMenuButton(
initialValue: _replyOption,
onSelected: (item) {
setState(() {
_replyOption = item;
});
},
itemBuilder: (context) => ReplyOption.values
.map(
(item) => PopupMenuItem<ReplyOption>(
value: item,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
item.iconData,
),
const SizedBox(width: 4),
Text(item.title),
],
),
),
)
.toList(),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
_replyOption.iconData,
color: _replyOption == ReplyOption.close
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 4),
Text(
_replyOption.title,
style: TextStyle(
height: 1,
color: _replyOption == ReplyOption.close
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
strutStyle: StrutStyle(leading: 0, height: 1),
),
Icon(
size: 20,
Icons.keyboard_arrow_right,
color: _replyOption == ReplyOption.close
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
],
),
),
),
const SizedBox(height: 5),
PopupMenuButton(
initialValue: _isPrivate,
onSelected: (value) {
setState(() {
_isPrivate = value;
});
},
itemBuilder: (context) => List.generate(
2,
(index) => PopupMenuItem<bool>(
enabled: _publishTime != null && index == 1
? false
: true,
value: index == 0 ? false : true,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
index == 0
? Icons.visibility
: Icons.visibility_off,
),
const SizedBox(width: 4),
Text(index == 0 ? '所有人可见' : '仅自己可见'),
],
),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
_isPrivate
? Icons.visibility_off
: Icons.visibility,
color: _isPrivate
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 4),
Text(
_isPrivate ? '仅自己可见' : '所有人可见',
style: TextStyle(
height: 1,
color: _isPrivate
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
strutStyle: StrutStyle(leading: 0, height: 1),
),
Icon(
size: 20,
Icons.keyboard_arrow_right,
color: _isPrivate
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
],
),
),
),
],
),
],
),
),
const SizedBox(height: 10),
Obx(
() => SizedBox(
height: 100,
child: ListView.separated(
scrollDirection: Axis.horizontal,
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: _pathList.length == _limit
? _limit
: _pathList.length + 1,
itemBuilder: (context, index) {
if (_pathList.length != _limit &&
index == _pathList.length) {
return Material(
borderRadius: BorderRadius.circular(12),
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () {
EasyThrottle.throttle('imagePicker',
const Duration(milliseconds: 500), () async {
try {
List<XFile> pickedFiles =
await _imagePicker.pickMultiImage(
limit: _limit,
imageQuality: 100,
);
if (pickedFiles.isNotEmpty) {
for (int i = 0; i < pickedFiles.length; i++) {
if (_pathList.length == _limit) {
SmartDialog.showToast('最多选择$_limit张图片');
break;
} else {
_pathList.add(pickedFiles[i].path);
}
}
if (_pathList.isNotEmpty &&
!_isEnablePub.value) {
_isEnablePub.value = true;
}
}
} catch (e) {
SmartDialog.showToast(e.toString());
}
});
},
child: Ink(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Theme.of(context)
.colorScheme
.secondaryContainer,
),
child: Center(child: Icon(Icons.add, size: 35)),
),
),
);
} else {
return GestureDetector(
onTap: () {
_pathList.removeAt(index);
if (_pathList.isEmpty && _ctr.text.trim().isEmpty) {
_isEnablePub.value = false;
}
},
child: Image(
height: 100,
fit: BoxFit.fitHeight,
filterQuality: FilterQuality.low,
image: FileImage(File(_pathList[index])),
),
);
}
},
separatorBuilder: (context, index) =>
const SizedBox(width: 10),
),
),
),
SizedBox(
height: MediaQuery.paddingOf(context).bottom + 25,
),
],
),
),
);
}
}

View File

@@ -0,0 +1,303 @@
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class RepostPanel extends StatefulWidget {
const RepostPanel({
super.key,
required this.item,
required this.callback,
});
final dynamic item;
final Function callback;
@override
State<RepostPanel> createState() => _RepostPanelState();
}
class _RepostPanelState extends State<RepostPanel> {
bool _isMax = false;
final _ctr = TextEditingController();
final _focusNode = FocusNode();
Future _onRepost() async {
dynamic result = await MsgHttp.createDynamic(
mid: GStorage.userInfo.get('userInfoCache')?.mid,
dynIdStr: widget.item.idStr,
rawText: _ctr.text,
);
if (result['status']) {
Get.back();
SmartDialog.showToast('转发成功');
widget.callback();
} else {
SmartDialog.showToast(result['msg']);
}
}
@override
void dispose() {
_ctr.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
dynamic pic = (widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.archive
?.cover ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.pgc
?.cover ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.opus
?.pics
?.firstOrNull
?.url;
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),
if (!_isMax)
Row(
children: [
const SizedBox(width: 16),
const Text(
'转发动态',
style: TextStyle(fontWeight: FontWeight.bold),
),
const Spacer(),
TextButton(
onPressed: _onRepost,
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
child: const Text('立即转发'),
),
const SizedBox(width: 16),
],
),
if (_isMax)
SizedBox(
height: 34,
child: Stack(
children: [
Positioned(
left: 16,
top: 0,
child: SizedBox(
width: 34,
height: 34,
child: IconButton(
tooltip: '返回',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor:
WidgetStateProperty.resolveWith((states) {
return Theme.of(context)
.colorScheme
.secondaryContainer;
}),
),
onPressed: Get.back,
icon: Icon(
Icons.arrow_back_outlined,
size: 18,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer,
),
),
),
),
Center(
child: const Text(
'转发动态',
style:
TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
),
Positioned(
right: 16,
top: 0,
child: FilledButton.tonal(
onPressed: _onRepost,
style: FilledButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
child: const Text('转发'),
),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
width: double.infinity,
decoration: !_isMax
? BoxDecoration(
border: Border(
left: BorderSide(
width: 2,
color: Theme.of(context).colorScheme.primary,
),
),
)
: null,
child: !_isMax
? GestureDetector(
onTap: () async {
setState(() => _isMax = true);
await Future.delayed(const Duration(milliseconds: 300));
if (mounted && context.mounted) {
_focusNode.requestFocus();
}
},
child: Text(
'说点什么吧',
style: TextStyle(
height: 1.75,
fontSize: 15,
color: Theme.of(context).colorScheme.outline,
),
),
)
: TextField(
controller: _ctr,
minLines: 4,
maxLines: 8,
focusNode: _focusNode,
decoration: const InputDecoration(
hintText: '说点什么吧',
border: OutlineInputBorder(
borderSide: BorderSide.none,
gapPadding: 0,
),
contentPadding: EdgeInsets.symmetric(vertical: 10),
),
),
),
),
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHigh ==
Theme.of(context).colorScheme.surface
? Theme.of(context).colorScheme.onInverseSurface
: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
if (pic != null) ...[
NetworkImgLayer(
radius: 8,
width: 40,
height: 40,
src: pic,
),
const SizedBox(width: 10),
],
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'@${(widget.item as DynamicItemModel?)?.modules?.moduleAuthor?.name}',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: 13,
),
),
Text(
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.opus
?.summary
?.text ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.desc
?.text ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.archive
?.title ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.pgc
?.title ??
'',
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
const SizedBox(height: 10),
if (!_isMax)
ListTile(
dense: true,
onTap: Get.back,
title: Center(
child: Text(
'取消',
style:
TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
),
SizedBox(height: 10 + MediaQuery.of(context).padding.bottom),
],
),
);
}
}

View File

@@ -1,18 +1,12 @@
import 'dart:async';
import 'dart:io';
import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/models/common/dynamics_type.dart';
import 'package:PiliPlus/models/common/up_panel_position.dart';
import 'package:PiliPlus/pages/dynamics/create_dyn_panel.dart';
import 'package:PiliPlus/pages/dynamics/tab/controller.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'controller.dart';
@@ -65,7 +59,7 @@ class _DynamicsPageState extends State<DynamicsPage>
context: context,
useSafeArea: true,
isScrollControlled: true,
builder: (context) => const CreatePanel(),
builder: (context) => const CreateDynPanel(),
);
}
},
@@ -198,519 +192,3 @@ class _DynamicsPageState extends State<DynamicsPage>
]));
}
}
class CreatePanel extends StatefulWidget {
const CreatePanel({super.key});
@override
State<CreatePanel> createState() => _CreatePanelState();
}
class _CreatePanelState extends State<CreatePanel> {
final _ctr = TextEditingController();
late final _imagePicker = ImagePicker();
late final int _limit = 18;
final RxBool _isEnablePub = false.obs;
late final RxList<String> _pathList = <String>[].obs;
bool _isPrivate = false;
DateTime? _publishTime;
ReplyOption _replyOption = ReplyOption.allow;
@override
void dispose() {
_ctr.dispose();
super.dispose();
}
Future _onCreate() async {
// if (_pathList.isEmpty) {
// dynamic result = await MsgHttp.createTextDynamic(_ctr.text);
// if (result['status']) {
// Get.back();
// SmartDialog.showToast('发布成功');
// } else {
// SmartDialog.showToast(result['msg']);
// }
// } else {
List? pics;
if (_pathList.isNotEmpty) {
pics = [];
for (int i = 0; i < _pathList.length; i++) {
SmartDialog.showLoading(msg: '正在上传图片: ${i + 1}/${_pathList.length}');
dynamic result = await MsgHttp.uploadBfs(
path: _pathList[i],
category: 'daily',
biz: 'new_dyn',
);
if (result['status']) {
int imageSize = await File(_pathList[i]).length();
pics.add({
'img_width': result['data']['image_width'],
'img_height': result['data']['image_height'],
'img_size': imageSize / 1024,
'img_src': result['data']['image_url'],
});
} else {
SmartDialog.dismiss();
SmartDialog.showToast(result['msg']);
return;
}
if (i == _pathList.length - 1) {
SmartDialog.dismiss();
}
}
}
SmartDialog.showLoading(msg: '正在发布');
dynamic result = await MsgHttp.createDynamic(
mid: GStorage.userInfo.get('userInfoCache')?.mid,
rawText: _ctr.text,
pics: pics,
publishTime: _publishTime != null
? _publishTime!.millisecondsSinceEpoch ~/ 1000
: null,
replyOption: _replyOption,
privatePub: _isPrivate ? 1 : null,
);
if (result['status']) {
Get.back();
SmartDialog.dismiss();
SmartDialog.showToast('发布成功');
} else {
SmartDialog.dismiss();
SmartDialog.showToast(result['msg']);
}
// }
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: true,
appBar: PreferredSize(
preferredSize: Size.fromHeight(66),
child: Padding(
padding: const EdgeInsets.all(16),
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
bottom: 0,
child: SizedBox(
width: 34,
height: 34,
child: IconButton(
tooltip: '返回',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.resolveWith(
(states) {
return Theme.of(context)
.colorScheme
.secondaryContainer;
},
),
),
onPressed: Get.back,
icon: Icon(
Icons.arrow_back_outlined,
size: 18,
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
),
),
Center(
child: const Text(
'发布动态',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
),
Positioned(
top: 0,
right: 0,
child: Obx(
() => FilledButton.tonal(
onPressed: _isEnablePub.value ? _onCreate : null,
style: FilledButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
child: Text(_publishTime == null ? '发布' : '定时发布'),
),
),
),
],
),
),
),
body: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextField(
controller: _ctr,
minLines: 4,
maxLines: 8,
autofocus: true,
onChanged: (value) {
bool isEmpty = value.trim().isEmpty && _pathList.isEmpty;
if (!isEmpty && !_isEnablePub.value) {
_isEnablePub.value = true;
} else if (isEmpty && _isEnablePub.value) {
_isEnablePub.value = false;
}
},
decoration: const InputDecoration(
hintText: '说点什么吧',
border: OutlineInputBorder(
borderSide: BorderSide.none,
gapPadding: 0,
),
contentPadding: EdgeInsets.zero,
),
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_publishTime == null
? FilledButton.tonal(
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
onPressed: _isPrivate
? null
: () {
DateTime nowDate = DateTime.now();
showDatePicker(
context: context,
initialDate: nowDate,
firstDate: nowDate,
lastDate: DateTime(
nowDate.year,
nowDate.month,
nowDate.day + 7,
),
).then(
(selectedDate) {
if (selectedDate != null &&
context.mounted) {
TimeOfDay nowTime = TimeOfDay.now();
showTimePicker(
context: context,
initialTime: nowTime.replacing(
hour: nowTime.minute + 6 >= 60
? (nowTime.hour + 1) % 24
: nowTime.hour,
minute: (nowTime.minute + 6) % 60,
),
).then((selectedTime) {
if (selectedTime != null) {
if (selectedDate.day ==
nowDate.day) {
if (selectedTime.hour <
nowTime.hour) {
SmartDialog.showToast(
'时间设置错误至少选择6分钟之后');
return;
} else if (selectedTime.hour ==
nowTime.hour) {
if (selectedTime.minute <
nowTime.minute + 6) {
if (selectedDate.day ==
nowDate.day) {
SmartDialog.showToast(
'时间设置错误至少选择6分钟之后');
}
return;
}
}
}
setState(() {
_publishTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
});
}
});
}
},
);
},
child: const Text('定时发布'),
)
: OutlinedButton.icon(
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
onPressed: () {
setState(() {
_publishTime = null;
});
},
label: Text(DateFormat('yyyy-MM-dd HH:mm')
.format(_publishTime!)),
icon: Icon(Icons.clear, size: 20),
iconAlignment: IconAlignment.end,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PopupMenuButton(
initialValue: _replyOption,
onSelected: (item) {
setState(() {
_replyOption = item;
});
},
itemBuilder: (context) => ReplyOption.values
.map(
(item) => PopupMenuItem<ReplyOption>(
value: item,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
item.iconData,
),
const SizedBox(width: 4),
Text(item.title),
],
),
),
)
.toList(),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
_replyOption.iconData,
color: _replyOption == ReplyOption.close
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 4),
Text(
_replyOption.title,
style: TextStyle(
height: 1,
color: _replyOption == ReplyOption.close
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
strutStyle: StrutStyle(leading: 0, height: 1),
),
Icon(
size: 20,
Icons.keyboard_arrow_right,
color: _replyOption == ReplyOption.close
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
],
),
),
),
const SizedBox(height: 5),
PopupMenuButton(
initialValue: _isPrivate,
onSelected: (value) {
setState(() {
_isPrivate = value;
});
},
itemBuilder: (context) => List.generate(
2,
(index) => PopupMenuItem<bool>(
enabled: _publishTime != null && index == 1
? false
: true,
value: index == 0 ? false : true,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
index == 0
? Icons.visibility
: Icons.visibility_off,
),
const SizedBox(width: 4),
Text(index == 0 ? '所有人可见' : '仅自己可见'),
],
),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
size: 19,
_isPrivate
? Icons.visibility_off
: Icons.visibility,
color: _isPrivate
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 4),
Text(
_isPrivate ? '仅自己可见' : '所有人可见',
style: TextStyle(
height: 1,
color: _isPrivate
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
strutStyle: StrutStyle(leading: 0, height: 1),
),
Icon(
size: 20,
Icons.keyboard_arrow_right,
color: _isPrivate
? Theme.of(context).colorScheme.error
: Theme.of(context).colorScheme.secondary,
),
],
),
),
),
],
),
],
),
),
const SizedBox(height: 10),
Obx(
() => SizedBox(
height: 100,
child: ListView.separated(
scrollDirection: Axis.horizontal,
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: _pathList.length == _limit
? _limit
: _pathList.length + 1,
itemBuilder: (context, index) {
if (_pathList.length != _limit &&
index == _pathList.length) {
return Material(
borderRadius: BorderRadius.circular(12),
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () {
EasyThrottle.throttle('imagePicker',
const Duration(milliseconds: 500), () async {
try {
List<XFile> pickedFiles =
await _imagePicker.pickMultiImage(
limit: _limit,
imageQuality: 100,
);
if (pickedFiles.isNotEmpty) {
for (int i = 0; i < pickedFiles.length; i++) {
if (_pathList.length == _limit) {
SmartDialog.showToast('最多选择$_limit张图片');
break;
} else {
_pathList.add(pickedFiles[i].path);
}
}
if (_pathList.isNotEmpty &&
!_isEnablePub.value) {
_isEnablePub.value = true;
}
}
} catch (e) {
SmartDialog.showToast(e.toString());
}
});
},
child: Ink(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Theme.of(context)
.colorScheme
.secondaryContainer,
),
child: Center(child: Icon(Icons.add, size: 35)),
),
),
);
} else {
return GestureDetector(
onTap: () {
_pathList.removeAt(index);
if (_pathList.isEmpty && _ctr.text.trim().isEmpty) {
_isEnablePub.value = false;
}
},
child: Image(
height: 100,
fit: BoxFit.fitHeight,
filterQuality: FilterQuality.low,
image: FileImage(File(_pathList[index])),
),
);
}
},
separatorBuilder: (context, index) =>
const SizedBox(width: 10),
),
),
),
SizedBox(
height: MediaQuery.paddingOf(context).bottom + 25,
),
],
),
),
);
}
}

View File

@@ -1,12 +1,9 @@
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/pages/dynamics/repost_dyn_panel.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/utils/feed_back.dart';
@@ -176,299 +173,3 @@ class _ActionPanelState extends State<ActionPanel> {
);
}
}
class RepostPanel extends StatefulWidget {
const RepostPanel({
super.key,
required this.item,
required this.callback,
});
final dynamic item;
final Function callback;
@override
State<RepostPanel> createState() => _RepostPanelState();
}
class _RepostPanelState extends State<RepostPanel> {
bool _isMax = false;
final _ctr = TextEditingController();
final _focusNode = FocusNode();
Future _onRepost() async {
dynamic result = await MsgHttp.createDynamic(
mid: GStorage.userInfo.get('userInfoCache')?.mid,
dynIdStr: widget.item.idStr,
rawText: _ctr.text,
);
if (result['status']) {
Get.back();
SmartDialog.showToast('转发成功');
widget.callback();
} else {
SmartDialog.showToast(result['msg']);
}
}
@override
void dispose() {
_ctr.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
dynamic pic = (widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.archive
?.cover ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.pgc
?.cover ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.opus
?.pics
?.firstOrNull
?.url;
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),
if (!_isMax)
Row(
children: [
const SizedBox(width: 16),
const Text(
'转发动态',
style: TextStyle(fontWeight: FontWeight.bold),
),
const Spacer(),
TextButton(
onPressed: _onRepost,
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
child: const Text('立即转发'),
),
const SizedBox(width: 16),
],
),
if (_isMax)
SizedBox(
height: 34,
child: Stack(
children: [
Positioned(
left: 16,
top: 0,
child: SizedBox(
width: 34,
height: 34,
child: IconButton(
tooltip: '返回',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor:
WidgetStateProperty.resolveWith((states) {
return Theme.of(context)
.colorScheme
.secondaryContainer;
}),
),
onPressed: Get.back,
icon: Icon(
Icons.arrow_back_outlined,
size: 18,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer,
),
),
),
),
Center(
child: const Text(
'转发动态',
style:
TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
),
Positioned(
right: 16,
top: 0,
child: FilledButton.tonal(
onPressed: _onRepost,
style: FilledButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -2,
),
),
child: const Text('转发'),
),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
width: double.infinity,
decoration: !_isMax
? BoxDecoration(
border: Border(
left: BorderSide(
width: 2,
color: Theme.of(context).colorScheme.primary,
),
),
)
: null,
child: !_isMax
? GestureDetector(
onTap: () async {
setState(() => _isMax = true);
await Future.delayed(const Duration(milliseconds: 300));
if (mounted && context.mounted) {
_focusNode.requestFocus();
}
},
child: Text(
'说点什么吧',
style: TextStyle(
height: 1.75,
fontSize: 15,
color: Theme.of(context).colorScheme.outline,
),
),
)
: TextField(
controller: _ctr,
minLines: 4,
maxLines: 8,
focusNode: _focusNode,
decoration: const InputDecoration(
hintText: '说点什么吧',
border: OutlineInputBorder(
borderSide: BorderSide.none,
gapPadding: 0,
),
contentPadding: EdgeInsets.symmetric(vertical: 10),
),
),
),
),
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHigh ==
Theme.of(context).colorScheme.surface
? Theme.of(context).colorScheme.onInverseSurface
: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
if (pic != null) ...[
NetworkImgLayer(
radius: 8,
width: 40,
height: 40,
src: pic,
),
const SizedBox(width: 10),
],
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'@${(widget.item as DynamicItemModel?)?.modules?.moduleAuthor?.name}',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: 13,
),
),
Text(
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.opus
?.summary
?.text ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.desc
?.text ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.archive
?.title ??
(widget.item as DynamicItemModel?)
?.modules
?.moduleDynamic
?.major
?.pgc
?.title ??
'',
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
const SizedBox(height: 10),
if (!_isMax)
ListTile(
dense: true,
onTap: Get.back,
title: Center(
child: Text(
'取消',
style:
TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
),
SizedBox(height: 10 + MediaQuery.of(context).padding.bottom),
],
),
);
}
}