mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-11 13:21:36 +08:00
feat: edit dyn
feat: set pub setting feat: set reply interaction Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
@@ -7,20 +7,28 @@ import 'package:PiliPlus/common/widgets/flutter/text_field/text_field.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/models/common/image_preview_type.dart';
|
||||
import 'package:PiliPlus/models/common/publish_panel_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart'
|
||||
show PicModel, FilePicModel, OpusPicModel;
|
||||
import 'package:PiliPlus/models_new/dynamic/dyn_mention/item.dart';
|
||||
import 'package:PiliPlus/models_new/emote/emote.dart' as e;
|
||||
import 'package:PiliPlus/models_new/live/live_emote/emoticon.dart';
|
||||
import 'package:PiliPlus/pages/common/publish/common_publish_page.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_mention/view.dart';
|
||||
import 'package:PiliPlus/utils/extension/file_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:dio/dio.dart' show CancelToken;
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
@@ -31,19 +39,21 @@ abstract class CommonRichTextPubPage
|
||||
const CommonRichTextPubPage({
|
||||
super.key,
|
||||
this.items,
|
||||
this.pics,
|
||||
super.onSave,
|
||||
super.autofocus,
|
||||
super.imageLengthLimit,
|
||||
});
|
||||
|
||||
final List<RichTextItem>? items;
|
||||
final List<PicModel>? pics;
|
||||
}
|
||||
|
||||
abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
extends CommonPublishPageState<T> {
|
||||
final key = GlobalKey<RichTextFieldState>();
|
||||
late final imagePicker = ImagePicker();
|
||||
late final RxList<String> pathList = <String>[].obs;
|
||||
late final RxList<PicModel> imageList;
|
||||
int get limit => widget.imageLengthLimit ?? 9;
|
||||
|
||||
@override
|
||||
@@ -58,13 +68,16 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
if (editController.rawText.trim().isNotEmpty) {
|
||||
enablePublish.value = true;
|
||||
}
|
||||
imageList = RxList<PicModel>(widget.pics ?? <PicModel>[]);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (PlatformUtils.isMobile) {
|
||||
for (final i in pathList) {
|
||||
File(i).tryDel();
|
||||
for (final img in imageList) {
|
||||
if (img is FilePicModel) {
|
||||
File(img.path).tryDel();
|
||||
}
|
||||
}
|
||||
}
|
||||
super.dispose();
|
||||
@@ -82,15 +95,18 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
).colorScheme.secondaryContainer.withValues(alpha: 0.5);
|
||||
|
||||
void onClear() {
|
||||
final path = pathList.removeAt(index);
|
||||
final image = imageList.removeAt(index);
|
||||
if (PlatformUtils.isMobile) {
|
||||
File(path).tryDel();
|
||||
if (image is FilePicModel) {
|
||||
File(image.path).tryDel();
|
||||
}
|
||||
}
|
||||
if (pathList.isEmpty && editController.rawText.trim().isEmpty) {
|
||||
if (imageList.isEmpty && editController.rawText.trim().isEmpty) {
|
||||
enablePublish.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
final image = imageList[index];
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
@@ -98,12 +114,18 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
onTap: () async {
|
||||
controller.keepChatPanel();
|
||||
await PageUtils.imageView(
|
||||
imgList: pathList
|
||||
imgList: imageList
|
||||
.map(
|
||||
(path) => SourceModel(
|
||||
url: path,
|
||||
sourceType: SourceType.fileImage,
|
||||
),
|
||||
(img) => switch (img) {
|
||||
FilePicModel e => SourceModel(
|
||||
url: e.path,
|
||||
sourceType: .fileImage,
|
||||
),
|
||||
OpusPicModel e => SourceModel(
|
||||
url: e.url!,
|
||||
sourceType: .networkImage,
|
||||
),
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
initialPage: index,
|
||||
@@ -117,21 +139,35 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
onSecondaryTap: PlatformUtils.isMobile ? null : onClear,
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
child: Image(
|
||||
height: height,
|
||||
fit: BoxFit.fitHeight,
|
||||
filterQuality: FilterQuality.low,
|
||||
image: FileImage(File(pathList[index])),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 42),
|
||||
child: switch (image) {
|
||||
FilePicModel e => Image.file(
|
||||
File(e.path),
|
||||
height: height,
|
||||
filterQuality: .low,
|
||||
cacheHeight: height.cacheSize(context),
|
||||
),
|
||||
OpusPicModel e => CachedNetworkImage(
|
||||
imageUrl: ImageUtils.thumbnailUrl(e.url!),
|
||||
height: height,
|
||||
filterQuality: .low,
|
||||
memCacheHeight: height.cacheSize(context),
|
||||
fadeInDuration: .zero,
|
||||
fadeOutDuration: .zero,
|
||||
placeholder: (_, _) => const SizedBox(width: 42),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (PlatformUtils.isMobile)
|
||||
if (kDebugMode || PlatformUtils.isMobile)
|
||||
Positioned(
|
||||
top: 34,
|
||||
right: 5,
|
||||
child: iconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () => onCropImage(index),
|
||||
onPressed: () => onCropImage(index, image),
|
||||
size: 24,
|
||||
iconSize: 14,
|
||||
bgColor: color,
|
||||
@@ -152,11 +188,23 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onCropImage(int index) async {
|
||||
Future<void> onCropImage(int index, PicModel image) async {
|
||||
String? path;
|
||||
switch (image) {
|
||||
case FilePicModel e:
|
||||
path = e.path;
|
||||
case OpusPicModel e:
|
||||
SmartDialog.showLoading();
|
||||
final file = (await DefaultCacheManager().getSingleFile(
|
||||
e.url.http2https,
|
||||
));
|
||||
await SmartDialog.dismiss();
|
||||
path = file.path;
|
||||
}
|
||||
if (!mounted || path.isEmpty) return;
|
||||
late final colorScheme = ColorScheme.of(context);
|
||||
final sourcePath = pathList[index];
|
||||
final croppedFile = await ImageCropper.platform.cropImage(
|
||||
sourcePath: sourcePath,
|
||||
sourcePath: path,
|
||||
uiSettings: [
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: '裁剪',
|
||||
@@ -168,8 +216,10 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
],
|
||||
);
|
||||
if (croppedFile != null) {
|
||||
File(sourcePath).tryDel();
|
||||
pathList[index] = croppedFile.path;
|
||||
if (image is FilePicModel) {
|
||||
File(image.path).tryDel();
|
||||
}
|
||||
imageList[index] = FilePicModel(path: croppedFile.path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,11 +235,11 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
);
|
||||
if (pickedFiles.isNotEmpty) {
|
||||
for (int i = 0; i < pickedFiles.length; i++) {
|
||||
if (pathList.length == limit) {
|
||||
if (imageList.length == limit) {
|
||||
SmartDialog.showToast('最多选择$limit张图片');
|
||||
break;
|
||||
} else {
|
||||
pathList.add(pickedFiles[i].path);
|
||||
imageList.add(FilePicModel(path: pickedFiles[i].path));
|
||||
}
|
||||
}
|
||||
callback?.call();
|
||||
@@ -465,25 +515,30 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
Future<void> onPublish() async {
|
||||
feedBack();
|
||||
List<Map<String, dynamic>>? pictures;
|
||||
if (pathList.isNotEmpty) {
|
||||
if (imageList.isNotEmpty) {
|
||||
SmartDialog.showLoading(msg: '正在上传图片...');
|
||||
final cancelToken = CancelToken();
|
||||
try {
|
||||
pictures = await Future.wait<Map<String, dynamic>>(
|
||||
pathList.map((path) async {
|
||||
final result = await MsgHttp.uploadBfs(
|
||||
path: path,
|
||||
category: 'daily',
|
||||
biz: 'new_dyn',
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
final data = result.data;
|
||||
return {
|
||||
'img_width': data.imageWidth,
|
||||
'img_height': data.imageHeight,
|
||||
'img_size': data.imgSize,
|
||||
'img_src': data.imageUrl,
|
||||
};
|
||||
imageList.map((img) async {
|
||||
switch (img) {
|
||||
case FilePicModel e:
|
||||
final result = await MsgHttp.uploadBfs(
|
||||
path: e.path,
|
||||
category: 'daily',
|
||||
biz: 'new_dyn',
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
final data = result.data;
|
||||
return {
|
||||
'img_width': data.imageWidth,
|
||||
'img_height': data.imageHeight,
|
||||
'img_size': data.imgSize,
|
||||
'img_src': data.imageUrl,
|
||||
};
|
||||
case OpusPicModel e:
|
||||
return e.toJson();
|
||||
}
|
||||
}),
|
||||
eagerError: true,
|
||||
);
|
||||
|
||||
@@ -5,6 +5,8 @@ import 'package:PiliPlus/common/widgets/dialog/report.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/reply.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
@@ -28,22 +30,27 @@ import 'package:get/get.dart';
|
||||
|
||||
class AuthorPanel extends StatelessWidget {
|
||||
final DynamicItemModel item;
|
||||
final Function? addBannedList;
|
||||
final bool isSave;
|
||||
final bool isDetail;
|
||||
final ValueChanged? onRemove;
|
||||
final ValueChanged<Object>? onRemove;
|
||||
final void Function(bool isTop, Object dynId)? onSetTop;
|
||||
final VoidCallback? onBlock;
|
||||
final Future<LoadingState> Function(bool isPrivate, Object dynId)?
|
||||
onSetPubSetting;
|
||||
final VoidCallback? onEdit;
|
||||
final ValueChanged<int>? onSetReplySubject;
|
||||
|
||||
const AuthorPanel({
|
||||
super.key,
|
||||
required this.item,
|
||||
this.addBannedList,
|
||||
this.isDetail = false,
|
||||
this.onRemove,
|
||||
this.isSave = false,
|
||||
this.onSetTop,
|
||||
this.onBlock,
|
||||
this.onSetPubSetting,
|
||||
this.onEdit,
|
||||
this.onSetReplySubject,
|
||||
});
|
||||
|
||||
Widget _buildAvatar(ModuleAuthorModel moduleAuthor) {
|
||||
@@ -73,6 +80,33 @@ class AuthorPanel extends StatelessWidget {
|
||||
)
|
||||
: DateFormatUtils.dateFormat(moduleAuthor.pubTs)
|
||||
: moduleAuthor.pubTime;
|
||||
Widget? pubTs;
|
||||
if (pubTime != null) {
|
||||
pubTs = Text(
|
||||
'$pubTime${moduleAuthor.pubAction != null ? ' ${moduleAuthor.pubAction}' : ''}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.outline,
|
||||
fontSize: theme.textTheme.labelSmall!.fontSize,
|
||||
),
|
||||
);
|
||||
if (moduleAuthor.badgeText case final badgeText?) {
|
||||
pubTs = Row(
|
||||
mainAxisSize: .min,
|
||||
spacing: 5,
|
||||
children: [
|
||||
pubTs,
|
||||
Text(
|
||||
badgeText,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
fontSize: theme.textTheme.labelSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
final moduleTagText = !isDetail ? item.modules.moduleTag?.text : null;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.center,
|
||||
@@ -88,10 +122,10 @@ class AuthorPanel extends StatelessWidget {
|
||||
}
|
||||
: null,
|
||||
child: Row(
|
||||
spacing: 10,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildAvatar(moduleAuthor),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -107,14 +141,7 @@ class AuthorPanel extends StatelessWidget {
|
||||
fontSize: theme.textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
if (pubTime != null)
|
||||
Text(
|
||||
'$pubTime${moduleAuthor.pubAction != null ? ' ${moduleAuthor.pubAction}' : ''}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.outline,
|
||||
fontSize: theme.textTheme.labelSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
?pubTs,
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -123,7 +150,7 @@ class AuthorPanel extends StatelessWidget {
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: !isDetail && item.modules.moduleTag?.text != null
|
||||
child: moduleTagText != null
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -142,7 +169,7 @@ class AuthorPanel extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
item.modules.moduleTag!.text!,
|
||||
moduleTagText,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 12,
|
||||
@@ -399,18 +426,150 @@ class AuthorPanel extends StatelessWidget {
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
onSetTop!(
|
||||
item.modules.moduleTag?.text != null,
|
||||
item.idStr,
|
||||
);
|
||||
onSetTop!(moduleAuthor.isTop ?? false, item.idStr);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(Icons.vertical_align_top, size: 19),
|
||||
title: Text(
|
||||
'${item.modules.moduleTag?.text != null ? '取消' : ''}置顶',
|
||||
'${moduleAuthor.isTop == true ? '取消' : ''}置顶',
|
||||
style: theme.textTheme.titleSmall!,
|
||||
),
|
||||
),
|
||||
if (onSetReplySubject != null)
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
Get.back();
|
||||
final res = await ReplyHttp.replyInteraction(
|
||||
oid: item.basic!.commentIdStr!,
|
||||
type: item.basic!.commentType!,
|
||||
);
|
||||
if (res case Success(:final response)) {
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final selection = response.upReplySelection;
|
||||
final enableSelection = selection.status == 1;
|
||||
|
||||
final reply = response.upReply;
|
||||
final enableReply = reply.status == 1;
|
||||
|
||||
return AlertDialog(
|
||||
clipBehavior: .hardEdge,
|
||||
contentPadding: const .symmetric(vertical: 12),
|
||||
content: Column(
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
enabled: selection.canModify,
|
||||
title: Text(
|
||||
'${enableSelection ? '停止' : '开启'}评论精选',
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
onSetReplySubject!(
|
||||
enableSelection ? 2 : 1,
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
enabled: reply.canModify,
|
||||
title: Text(
|
||||
'${enableReply ? '关闭' : '恢复'}评论',
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
onSetReplySubject!(enableReply ? 3 : 4);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
res.toast();
|
||||
}
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(
|
||||
Icons.mark_unread_chat_alt_outlined,
|
||||
size: 19,
|
||||
),
|
||||
title: Text(
|
||||
'互动设置',
|
||||
style: theme.textTheme.titleSmall!,
|
||||
),
|
||||
),
|
||||
if (onSetPubSetting != null)
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
|
||||
final isPrivate = moduleAuthor.badgeText != null;
|
||||
Future<void> onTap() async {
|
||||
Get.back();
|
||||
if ((await onSetPubSetting!(
|
||||
isPrivate,
|
||||
item.idStr,
|
||||
)).isSuccess) {
|
||||
if (context.mounted) {
|
||||
(context as Element).markNeedsBuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
contentPadding: const .symmetric(vertical: 12),
|
||||
content: Column(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
enabled: isPrivate,
|
||||
title: const Text(
|
||||
'所有用户可见',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: onTap,
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
enabled: !isPrivate,
|
||||
title: const Text(
|
||||
'仅自己可见',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: onTap,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(Icons.visibility, size: 19),
|
||||
title: Text('可见范围', style: theme.textTheme.titleSmall!),
|
||||
),
|
||||
if (onEdit != null)
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
onEdit!();
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(Icons.edit_note, size: 19),
|
||||
title: Text('编辑动态', style: theme.textTheme.titleSmall!),
|
||||
),
|
||||
if (onRemove != null)
|
||||
ListTile(
|
||||
onTap: () {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/widgets/avatars.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/action_panel.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/author_panel.dart';
|
||||
@@ -15,12 +16,16 @@ class DynamicPanel extends StatelessWidget {
|
||||
final DynamicItemModel item;
|
||||
final double maxWidth;
|
||||
final bool isDetail;
|
||||
final ValueChanged? onRemove;
|
||||
final ValueChanged<Object>? onRemove;
|
||||
final bool isSave;
|
||||
final void Function(bool isTop, Object dynId)? onSetTop;
|
||||
final VoidCallback? onBlock;
|
||||
final VoidCallback? onUnfold;
|
||||
final bool isDetailPortraitW;
|
||||
final Future<LoadingState> Function(bool isPrivate, Object dynId)?
|
||||
onSetPubSetting;
|
||||
final VoidCallback? onEdit;
|
||||
final ValueChanged<int>? onSetReplySubject;
|
||||
|
||||
const DynamicPanel({
|
||||
super.key,
|
||||
@@ -33,6 +38,9 @@ class DynamicPanel extends StatelessWidget {
|
||||
this.onBlock,
|
||||
this.onUnfold,
|
||||
this.isDetailPortraitW = true,
|
||||
this.onSetPubSetting,
|
||||
this.onEdit,
|
||||
this.onSetReplySubject,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -48,6 +56,9 @@ class DynamicPanel extends StatelessWidget {
|
||||
isSave: isSave,
|
||||
onSetTop: onSetTop,
|
||||
onBlock: onBlock,
|
||||
onSetPubSetting: onSetPubSetting,
|
||||
onEdit: onEdit,
|
||||
onSetReplySubject: onSetReplySubject,
|
||||
);
|
||||
|
||||
void showMore() => _imageSaveDialog(context, authorWidget.morePanel);
|
||||
|
||||
@@ -35,10 +35,13 @@ TextSpan? richNode(
|
||||
if (richTextNodes == null || richTextNodes.isEmpty) {
|
||||
return TextSpan(text: desc.text);
|
||||
}
|
||||
} else if (moduleDynamic?.major?.opus case final opus?) {
|
||||
} else if (moduleDynamic?.major?.opus case DynamicOpusModel(
|
||||
:final title,
|
||||
:final summary,
|
||||
)) {
|
||||
// 动态页面 richTextNodes 层级可能与主页动态层级不同
|
||||
richTextNodes = opus.summary?.richTextNodes;
|
||||
if (opus.title case final title?) {
|
||||
richTextNodes = summary?.richTextNodes;
|
||||
if (title != null && title.isNotEmpty) {
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: '$title\n',
|
||||
@@ -76,7 +79,7 @@ TextSpan? richNode(
|
||||
case 'RICH_TEXT_NODE_TYPE_TOPIC':
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: i.origText!,
|
||||
text: i.origText,
|
||||
style: style,
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => Get.toNamed(
|
||||
|
||||
@@ -10,8 +10,10 @@ import 'package:PiliPlus/common/widgets/flutter/text_field/controller.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/text_field/text_field.dart';
|
||||
import 'package:PiliPlus/common/widgets/pair.dart';
|
||||
import 'package:PiliPlus/http/dynamics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/publish_panel_type.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_option_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart' show PicModel;
|
||||
import 'package:PiliPlus/models/dynamics/vote_model.dart';
|
||||
import 'package:PiliPlus/models_new/dynamic/dyn_reserve_info/data.dart';
|
||||
import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/topic_item.dart';
|
||||
@@ -29,7 +31,6 @@ import 'package:PiliPlus/utils/extension/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
|
||||
import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
@@ -39,48 +40,81 @@ class CreateDynPanel extends CommonRichTextPubPage {
|
||||
const CreateDynPanel({
|
||||
super.key,
|
||||
super.imageLengthLimit = 18,
|
||||
super.items,
|
||||
super.pics,
|
||||
this.scrollController,
|
||||
this.topic,
|
||||
this.editConfig,
|
||||
this.title,
|
||||
this.isPrivate = false,
|
||||
this.replyOption = .allow,
|
||||
this.onSuccess,
|
||||
});
|
||||
|
||||
final ScrollController? scrollController;
|
||||
final String? title;
|
||||
final Pair<int, String>? topic;
|
||||
final bool isPrivate;
|
||||
final ReplyOptionType replyOption;
|
||||
final ({Object dynId, Object? repostDynId})? editConfig;
|
||||
final VoidCallback? onSuccess;
|
||||
|
||||
@override
|
||||
State<CreateDynPanel> createState() => _CreateDynPanelState();
|
||||
|
||||
static void onCreateDyn(BuildContext context, {Pair<int, String>? topic}) =>
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => dyn_sheet.DraggableScrollableSheet(
|
||||
snap: true,
|
||||
expand: false,
|
||||
initialChildSize: 1,
|
||||
minChildSize: 0,
|
||||
maxChildSize: 1,
|
||||
snapSizes: const [1],
|
||||
builder: (context, scrollController) => CreateDynPanel(
|
||||
scrollController: scrollController,
|
||||
topic: topic,
|
||||
),
|
||||
),
|
||||
);
|
||||
static void onCreateDyn(
|
||||
BuildContext context, {
|
||||
String? title,
|
||||
bool isPrivate = false,
|
||||
ReplyOptionType replyOption = .allow,
|
||||
List<RichTextItem>? items,
|
||||
List<PicModel>? pics,
|
||||
Pair<int, String>? topic,
|
||||
({Object dynId, Object? repostDynId})? editConfig,
|
||||
VoidCallback? onSuccess,
|
||||
}) => showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => dyn_sheet.DraggableScrollableSheet(
|
||||
snap: true,
|
||||
expand: false,
|
||||
initialChildSize: 1,
|
||||
minChildSize: 0,
|
||||
maxChildSize: 1,
|
||||
snapSizes: const [1],
|
||||
builder: (context, scrollController) => CreateDynPanel(
|
||||
scrollController: scrollController,
|
||||
title: title,
|
||||
items: items,
|
||||
pics: pics,
|
||||
topic: topic,
|
||||
isPrivate: isPrivate,
|
||||
editConfig: editConfig,
|
||||
replyOption: replyOption,
|
||||
onSuccess: onSuccess,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
final RxBool _isPrivate = false.obs;
|
||||
final Rx<DateTime?> _publishTime = Rx<DateTime?>(null);
|
||||
final Rx<ReplyOptionType> _replyOption = ReplyOptionType.allow.obs;
|
||||
final _titleEditCtr = TextEditingController();
|
||||
final Rx<Pair<int, String>?> topic = Rx<Pair<int, String>?>(null);
|
||||
late final bool _isEdit;
|
||||
late final RxBool _isPrivate;
|
||||
late final Rx<Pair<int, String>?> _topic;
|
||||
late final Rx<ReplyOptionType> _replyOption;
|
||||
late final TextEditingController _titleEditCtr;
|
||||
late final Rx<DateTime?> _publishTime = Rx<DateTime?>(null);
|
||||
final Rx<ReserveInfoData?> _reserveCard = Rx<ReserveInfoData?>(null);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
topic.value = widget.topic;
|
||||
_isEdit = widget.editConfig != null;
|
||||
_isPrivate = widget.isPrivate.obs;
|
||||
_replyOption = widget.replyOption.obs;
|
||||
_topic = Rx<Pair<int, String>?>(widget.topic);
|
||||
_titleEditCtr = TextEditingController(text: widget.title);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -110,7 +144,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Obx(
|
||||
() {
|
||||
final hasTopic = topic.value != null;
|
||||
final hasTopic = _topic.value != null;
|
||||
return Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
@@ -158,7 +192,9 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: hasTopic ? topic.value!.second : '选择话题',
|
||||
text: hasTopic
|
||||
? _topic.value!.second
|
||||
: '选择话题',
|
||||
style: TextStyle(
|
||||
color: hasTopic
|
||||
? null
|
||||
@@ -176,7 +212,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
icon: const Icon(Icons.clear),
|
||||
bgColor: theme.colorScheme.onInverseSurface,
|
||||
iconColor: theme.colorScheme.onSurfaceVariant,
|
||||
onPressed: () => topic.value = null,
|
||||
onPressed: () => _topic.value = null,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -253,10 +289,10 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...List.generate(
|
||||
pathList.length,
|
||||
imageList.length,
|
||||
(index) => buildImage(index, 100),
|
||||
),
|
||||
if (pathList.length != limit)
|
||||
if (imageList.length != limit)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
const borderRadius = StyleString.mdRadius;
|
||||
@@ -265,7 +301,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: () => onPickImage(() {
|
||||
if (pathList.isNotEmpty && !enablePublish.value) {
|
||||
if (imageList.isNotEmpty && !enablePublish.value) {
|
||||
enablePublish.value = true;
|
||||
}
|
||||
}),
|
||||
@@ -316,10 +352,10 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const Center(
|
||||
Center(
|
||||
child: Text(
|
||||
'发布动态',
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
||||
_isEdit ? '编辑动态' : '发布动态',
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
@@ -464,7 +500,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: _isPrivate.value
|
||||
onPressed: _isEdit || _isPrivate.value
|
||||
? null
|
||||
: () async {
|
||||
controller.keepChatPanel();
|
||||
@@ -538,8 +574,10 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
children: [
|
||||
emojiBtn,
|
||||
atBtn,
|
||||
voteBtn,
|
||||
moreBtn,
|
||||
if (!_isEdit) ...[
|
||||
voteBtn,
|
||||
moreBtn,
|
||||
],
|
||||
// if (kDebugMode)
|
||||
// ToolbarIconButton(
|
||||
// onPressed: editController.clear,
|
||||
@@ -707,8 +745,34 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
SmartDialog.showLoading(msg: '正在发布');
|
||||
List<Map<String, dynamic>>? extraContent = getRichContent();
|
||||
final hasRichText = extraContent != null;
|
||||
|
||||
if (_isEdit) {
|
||||
final editConfig = widget.editConfig!;
|
||||
final res = await DynamicsHttp.editDyn(
|
||||
dynId: editConfig.dynId,
|
||||
repostDynId: editConfig.repostDynId,
|
||||
rawText: hasRichText ? null : editController.text,
|
||||
pics: pictures,
|
||||
replyOption: _replyOption.value,
|
||||
privatePub: _isPrivate.value ? 1 : null,
|
||||
title: _titleEditCtr.text,
|
||||
topic: _topic.value,
|
||||
extraContent: extraContent,
|
||||
);
|
||||
SmartDialog.dismiss();
|
||||
if (res.isSuccess) {
|
||||
hasPub = true;
|
||||
Get.back();
|
||||
SmartDialog.showToast('发布成功');
|
||||
widget.onSuccess?.call();
|
||||
} else {
|
||||
res.toast();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final reserveCard = _reserveCard.value;
|
||||
final result = await DynamicsHttp.createDynamic(
|
||||
final res = await DynamicsHttp.createDynamic(
|
||||
mid: Accounts.main.mid,
|
||||
rawText: hasRichText ? null : editController.text,
|
||||
pics: pictures,
|
||||
@@ -718,7 +782,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
replyOption: _replyOption.value,
|
||||
privatePub: _isPrivate.value ? 1 : null,
|
||||
title: _titleEditCtr.text,
|
||||
topic: topic.value,
|
||||
topic: _topic.value,
|
||||
extraContent: extraContent,
|
||||
attachCard: reserveCard == null
|
||||
? null
|
||||
@@ -732,11 +796,11 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
},
|
||||
);
|
||||
SmartDialog.dismiss();
|
||||
if (result['status']) {
|
||||
if (res case Success(:final response)) {
|
||||
hasPub = true;
|
||||
Get.back();
|
||||
SmartDialog.showToast('发布成功');
|
||||
final id = result['data']?['dyn_id'];
|
||||
final id = response?['dyn_id'];
|
||||
RequestUtils.insertCreatedDyn(id);
|
||||
if (!_isPrivate.value) {
|
||||
RequestUtils.checkCreatedDyn(
|
||||
@@ -745,8 +809,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
if (kDebugMode) debugPrint('failed to publish: ${result['msg']}');
|
||||
res.toast();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,7 +822,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
|
||||
onCachePos: (offset) => _topicOffset = offset,
|
||||
);
|
||||
if (res != null) {
|
||||
topic.value = Pair(first: res.id, second: res.name);
|
||||
_topic.value = Pair(first: res.id, second: res.name);
|
||||
}
|
||||
controller.restoreChatPanel();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:PiliPlus/http/dynamics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/reply.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/common/dyn/common_dyn_controller.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class DynamicDetailController extends CommonDynController {
|
||||
@@ -45,4 +47,33 @@ class DynamicDetailController extends CommonDynController {
|
||||
replyType = commentType;
|
||||
queryData();
|
||||
}
|
||||
|
||||
Future<LoadingState> onSetPubSetting(bool isPrivate, Object dynId) async {
|
||||
final res = await DynamicsHttp.dynPrivatePubSetting(
|
||||
dynId: dynId,
|
||||
action: isPrivate ? 'public_pub' : 'private_pub',
|
||||
);
|
||||
if (res.isSuccess) {
|
||||
dynItem.modules.moduleAuthor?.badgeText = isPrivate ? null : '仅自己可见';
|
||||
SmartDialog.showToast('设置成功');
|
||||
} else {
|
||||
res.toast();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<void> onSetReplySubject(int action) async {
|
||||
final res = await ReplyHttp.replySubjectModify(
|
||||
oid: oid,
|
||||
type: replyType,
|
||||
action: action,
|
||||
);
|
||||
if (res.isSuccess) {
|
||||
await Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (!isClosed) {
|
||||
onReload();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,17 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/custom_icon.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/text_field/controller.dart';
|
||||
import 'package:PiliPlus/common/widgets/pair.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/dynamics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_option_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/common/dyn/common_dyn_page.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/author_panel.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_create/view.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_detail/controller.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_repost/view.dart';
|
||||
import 'package:PiliPlus/utils/extension/get_ext.dart';
|
||||
@@ -14,6 +20,7 @@ import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/num_utils.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -66,6 +73,142 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
||||
);
|
||||
}
|
||||
|
||||
void _onEdit() {
|
||||
final item = controller.dynItem;
|
||||
List<RichTextItem>? items;
|
||||
final moduleDynamic = item.modules.moduleDynamic;
|
||||
final desc = moduleDynamic?.desc;
|
||||
final opus = moduleDynamic?.major?.opus;
|
||||
|
||||
Pair<int, String>? topic;
|
||||
if (moduleDynamic?.topic case final t?) {
|
||||
try {
|
||||
topic = Pair(first: t.id!, second: t.name!);
|
||||
} catch (_) {
|
||||
if (kDebugMode) rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
final richTextNodes = desc?.richTextNodes ?? opus?.summary?.richTextNodes;
|
||||
if (richTextNodes != null && richTextNodes.isNotEmpty) {
|
||||
items = <RichTextItem>[];
|
||||
final buffer = StringBuffer();
|
||||
try {
|
||||
for (final e in richTextNodes) {
|
||||
if (e.type == 'RICH_TEXT_NODE_TYPE_EMOJI') {
|
||||
const placeHolder = '\uFFFC';
|
||||
items.add(
|
||||
RichTextItem(
|
||||
text: placeHolder,
|
||||
rawText: e.origText,
|
||||
type: .emoji,
|
||||
range: TextRange(
|
||||
start: buffer.length,
|
||||
end: buffer.length + placeHolder.length,
|
||||
),
|
||||
emote: Emote(
|
||||
url: e.emoji!.url!,
|
||||
width: 22,
|
||||
),
|
||||
),
|
||||
);
|
||||
buffer.write(placeHolder);
|
||||
continue;
|
||||
}
|
||||
final range = TextRange(
|
||||
start: buffer.length,
|
||||
end: buffer.length + e.origText!.length,
|
||||
);
|
||||
final item = switch (e.type) {
|
||||
'RICH_TEXT_NODE_TYPE_AT' => RichTextItem(
|
||||
text: e.origText!,
|
||||
type: .at,
|
||||
range: range,
|
||||
id: e.rid,
|
||||
),
|
||||
'RICH_TEXT_NODE_TYPE_BV' ||
|
||||
'RICH_TEXT_NODE_TYPE_TOPIC' ||
|
||||
'RICH_TEXT_NODE_TYPE_LOTTERY' ||
|
||||
'RICH_TEXT_NODE_TYPE_VIEW_PICTURE' => RichTextItem(
|
||||
text: e.origText!,
|
||||
type: .common,
|
||||
range: range,
|
||||
id: e.rid,
|
||||
),
|
||||
'RICH_TEXT_NODE_TYPE_VOTE' => RichTextItem(
|
||||
text: e.origText!,
|
||||
type: .vote,
|
||||
range: range,
|
||||
id: e.rid,
|
||||
),
|
||||
_ => RichTextItem(
|
||||
text: e.origText!,
|
||||
range: range,
|
||||
),
|
||||
};
|
||||
items.add(item);
|
||||
buffer.write(e.origText!);
|
||||
}
|
||||
|
||||
bool isValid = true;
|
||||
int cursor = 0;
|
||||
for (final e in items) {
|
||||
final range = e.range;
|
||||
if (range.start == cursor) {
|
||||
cursor = range.end;
|
||||
} else {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(isValid);
|
||||
} catch (e) {
|
||||
if (kDebugMode) rethrow;
|
||||
}
|
||||
} else {
|
||||
final text = desc?.text ?? opus?.summary?.text;
|
||||
if (text != null && text.isNotEmpty) {
|
||||
items = [
|
||||
RichTextItem.fromStart(text),
|
||||
];
|
||||
}
|
||||
}
|
||||
ReplyOptionType? replyOption;
|
||||
if (controller.loadingState.value case Error(:final code)) {
|
||||
if (code == 12061 || code == 12002) {
|
||||
replyOption = .close;
|
||||
}
|
||||
}
|
||||
CreateDynPanel.onCreateDyn(
|
||||
context,
|
||||
title: opus?.title,
|
||||
items: items,
|
||||
pics: opus?.pics,
|
||||
topic: topic,
|
||||
replyOption: replyOption ?? .allow,
|
||||
isPrivate: item.modules.moduleAuthor?.badgeText != null,
|
||||
editConfig: (
|
||||
dynId: item.idStr,
|
||||
repostDynId: item.orig?.idStr,
|
||||
),
|
||||
onSuccess: () {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 500),
|
||||
() async {
|
||||
if (!mounted) return;
|
||||
final res = await DynamicsHttp.dynamicDetail(id: item.idStr);
|
||||
if (res case Success(:final response)) {
|
||||
if (mounted) {
|
||||
controller.dynItem = response;
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() => AppBar(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
@@ -80,6 +223,9 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
||||
child: AuthorPanel(
|
||||
item: controller.dynItem,
|
||||
isDetail: true,
|
||||
onSetPubSetting: controller.onSetPubSetting,
|
||||
onEdit: _onEdit,
|
||||
onSetReplySubject: controller.onSetReplySubject,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -88,10 +234,7 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
||||
),
|
||||
actions: isPortrait
|
||||
? null
|
||||
: [
|
||||
ratioWidget(maxWidth),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
: [ratioWidget(maxWidth), const SizedBox(width: 16)],
|
||||
);
|
||||
|
||||
Widget _buildBody(ThemeData theme) {
|
||||
@@ -110,6 +253,9 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
||||
isDetail: true,
|
||||
maxWidth: maxWidth - this.padding.horizontal - 2 * padding,
|
||||
isDetailPortraitW: isPortrait,
|
||||
onSetPubSetting: controller.onSetPubSetting,
|
||||
onEdit: _onEdit,
|
||||
onSetReplySubject: controller.onSetReplySubject,
|
||||
),
|
||||
),
|
||||
buildReplyHeader(theme),
|
||||
@@ -143,6 +289,9 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
||||
(flex / (flex + flex1)) -
|
||||
padding,
|
||||
isDetailPortraitW: isPortrait,
|
||||
onSetPubSetting: controller.onSetPubSetting,
|
||||
onEdit: _onEdit,
|
||||
onSetReplySubject: controller.onSetReplySubject,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/common/widgets/flutter/draggable_sheet/draggable_scroll
|
||||
import 'package:PiliPlus/common/widgets/flutter/text_field/text_field.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/dynamics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/publish_panel_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/common/publish/common_rich_text_pub_page.dart';
|
||||
@@ -419,7 +420,7 @@ class _RepostPanelState extends CommonRichTextPubPageState<RepostPanel> {
|
||||
if (hasRichText && repostContent != null) {
|
||||
richContent.addAll(repostContent);
|
||||
}
|
||||
final result = await DynamicsHttp.createDynamic(
|
||||
final res = await DynamicsHttp.createDynamic(
|
||||
mid: Accounts.main.mid,
|
||||
dynIdStr: widget.item?.idStr ?? widget.dynIdStr,
|
||||
rid: widget.rid,
|
||||
@@ -428,19 +429,19 @@ class _RepostPanelState extends CommonRichTextPubPageState<RepostPanel> {
|
||||
extraContent: richContent ?? repostContent,
|
||||
);
|
||||
SmartDialog.dismiss();
|
||||
if (result['status']) {
|
||||
if (res case Success(:final response)) {
|
||||
hasPub = true;
|
||||
Get.back();
|
||||
SmartDialog.showToast('转发成功');
|
||||
widget.onSuccess?.call();
|
||||
final id = result['data']?['dyn_id'];
|
||||
final id = response?['dyn_id'];
|
||||
RequestUtils.insertCreatedDyn(id);
|
||||
RequestUtils.checkCreatedDyn(
|
||||
id: id,
|
||||
dynText: editController.rawText,
|
||||
);
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
res.toast();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/main.dart';
|
||||
import 'package:PiliPlus/models/common/publish_panel_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart' show FilePicModel;
|
||||
import 'package:PiliPlus/pages/common/publish/common_rich_text_pub_page.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_mention/controller.dart';
|
||||
import 'package:PiliPlus/pages/emote/view.dart';
|
||||
@@ -110,7 +111,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
Widget buildImagePreview() {
|
||||
return Obx(
|
||||
() {
|
||||
if (pathList.isNotEmpty) {
|
||||
if (imageList.isNotEmpty) {
|
||||
return SizedBox(
|
||||
height: 85,
|
||||
child: SingleChildScrollView(
|
||||
@@ -120,7 +121,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
spacing: 10,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: List.generate(
|
||||
pathList.length,
|
||||
imageList.length,
|
||||
(index) => buildImage(index, 75),
|
||||
),
|
||||
),
|
||||
@@ -368,7 +369,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
if (isRoot && widget.canUploadPic)
|
||||
item(
|
||||
onTap: () async {
|
||||
if (pathList.length >= limit) {
|
||||
if (imageList.length >= limit) {
|
||||
SmartDialog.showToast('最多选择$limit张图片');
|
||||
return;
|
||||
}
|
||||
@@ -385,7 +386,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
'$tmpDirPath/${Utils.generateRandomString(8)}.png',
|
||||
);
|
||||
await file.writeAsBytes(res);
|
||||
pathList.add(file.path);
|
||||
imageList.add(FilePicModel(path: file.path));
|
||||
} else {
|
||||
debugPrint('null screenshot');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user