Files
PiliPlus/lib/pages/dynamics/widgets/dynamic_panel.dart
dom cb58822009 feat: edit dyn
feat: set pub setting

feat: set reply interaction

Signed-off-by: dom <githubaccount56556@proton.me>
2026-01-15 15:03:19 +08:00

309 lines
8.9 KiB
Dart

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';
import 'package:PiliPlus/pages/dynamics/widgets/dyn_content.dart';
import 'package:PiliPlus/pages/dynamics/widgets/interaction.dart';
import 'package:PiliPlus/utils/extension/theme_ext.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:flutter/material.dart' hide InkWell;
class DynamicPanel extends StatelessWidget {
final DynamicItemModel item;
final double maxWidth;
final bool isDetail;
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,
required this.item,
required this.maxWidth,
this.isDetail = false,
this.onRemove,
this.isSave = false,
this.onSetTop,
this.onBlock,
this.onUnfold,
this.isDetailPortraitW = true,
this.onSetPubSetting,
this.onEdit,
this.onSetReplySubject,
});
@override
Widget build(BuildContext context) {
if (item.visible == false) {
return const SizedBox.shrink();
}
final theme = Theme.of(context);
final authorWidget = AuthorPanel(
item: item,
isDetail: isDetail,
onRemove: onRemove,
isSave: isSave,
onSetTop: onSetTop,
onBlock: onBlock,
onSetPubSetting: onSetPubSetting,
onEdit: onEdit,
onSetReplySubject: onSetReplySubject,
);
void showMore() => _imageSaveDialog(context, authorWidget.morePanel);
final child = Material(
type: MaterialType.transparency,
child: InkWell(
onTap:
isDetail &&
!const {
'DYNAMIC_TYPE_AV',
'DYNAMIC_TYPE_UGC_SEASON',
'DYNAMIC_TYPE_PGC_UNION',
'DYNAMIC_TYPE_PGC',
'DYNAMIC_TYPE_LIVE',
'DYNAMIC_TYPE_LIVE_RCMD',
'DYNAMIC_TYPE_MEDIALIST',
'DYNAMIC_TYPE_COURSES_SEASON',
}.contains(item.type)
? null
: () => PageUtils.pushDynDetail(item),
onLongPress: showMore,
onSecondaryTap: PlatformUtils.isMobile ? null : showMore,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(12, 12, 12, 6),
child: authorWidget,
),
if (item.modules.moduleDispute case final moduleDispute?)
_buildDispute(theme, moduleDispute),
...dynContent(
context,
theme: theme,
isSave: isSave,
isDetail: isDetail,
item: item,
floor: 1,
maxWidth: maxWidth,
),
const SizedBox(height: 2),
if (!isDetail) ...[
if (item.modules.moduleInteraction case ModuleInteraction(
:final items,
))
if (items != null && items.isNotEmpty)
dynInteraction(
theme: theme,
items: items,
),
ActionPanel(item: item),
if (item.modules.moduleFold case final moduleFold?) ...[
Divider(
height: 1,
color: theme.dividerColor.withValues(alpha: 0.1),
),
_buildFoldItem(theme, moduleFold),
],
] else if (!isSave)
const SizedBox(height: 12),
],
),
),
);
if (isSave || (isDetail && !isDetailPortraitW)) {
return child;
}
return DecoratedBox(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 8,
color: theme.dividerColor.withValues(alpha: 0.05),
),
),
),
child: Padding(
padding: const EdgeInsets.only(bottom: 8),
child: child,
),
);
}
void _imageSaveDialog(
BuildContext context,
Function(BuildContext) morePanel,
) {
String? title;
String? cover;
String? bvid;
late final major = item.modules.moduleDynamic?.major;
switch (item.type) {
case 'DYNAMIC_TYPE_AV':
if (major?.archive case final archive?) {
title = archive.title;
cover = archive.cover;
bvid = archive.bvid;
}
break;
case 'DYNAMIC_TYPE_UGC_SEASON':
if (major?.ugcSeason case final ugcSeason?) {
title = ugcSeason.title;
cover = ugcSeason.cover;
bvid = ugcSeason.bvid;
}
break;
case 'DYNAMIC_TYPE_PGC' || 'DYNAMIC_TYPE_PGC_UNION':
if (major?.pgc case final pgc?) {
title = pgc.title;
cover = pgc.cover;
}
break;
case 'DYNAMIC_TYPE_LIVE_RCMD':
if (major?.liveRcmd case final liveRcmd?) {
title = liveRcmd.title;
cover = liveRcmd.cover;
}
break;
case 'DYNAMIC_TYPE_LIVE':
if (major?.live case final live?) {
title = live.title;
cover = live.cover;
}
break;
case 'DYNAMIC_TYPE_COURSES_SEASON':
if (major?.courses case final courses?) {
title = courses.title;
cover = courses.cover;
}
break;
case 'DYNAMIC_TYPE_SUBSCRIPTION_NEW':
if (major?.subscriptionNew?.liveRcmd?.content?.livePlayInfo
case final livePlayInfo?) {
title = livePlayInfo.title;
cover = livePlayInfo.cover;
}
break;
default:
morePanel(context);
return;
}
imageSaveDialog(
title: title,
cover: cover,
bvid: bvid,
);
}
Widget _buildFoldItem(ThemeData theme, ModuleFold moduleFold) {
Widget child = Text.rich(
textAlign: TextAlign.center,
style: TextStyle(
height: 1,
fontSize: 13,
color: theme.colorScheme.outline,
),
strutStyle: const StrutStyle(
height: 1,
leading: 0,
fontSize: 13,
),
TextSpan(
children: [
TextSpan(text: moduleFold.statement ?? '展开'),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
size: 19,
Icons.keyboard_arrow_down,
color: theme.colorScheme.outline,
),
),
],
),
);
final users = moduleFold.users;
if (users != null && users.isNotEmpty) {
child = Row(
spacing: 5,
mainAxisAlignment: .center,
children: [
avatars(colorScheme: theme.colorScheme, users: users),
child,
],
);
}
return InkWell(
onTap: onUnfold,
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 10),
child: child,
),
);
}
Widget _buildDispute(ThemeData theme, ModuleDispute moduleDispute) {
final child = Container(
width: .infinity,
margin: const .fromLTRB(12, 2, 12, 6),
padding: const .symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
color: theme.colorScheme.secondaryContainer.withValues(
alpha: theme.brightness.isLight ? 0.5 : 0.7,
),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
child: Text.rich(
style: TextStyle(
height: 1,
fontSize: 13,
color: theme.colorScheme.onSecondaryContainer,
),
strutStyle: const StrutStyle(
leading: 0,
height: 1,
fontSize: 13,
),
TextSpan(
children: [
WidgetSpan(
alignment: .middle,
child: Padding(
padding: const .only(right: 4),
child: Icon(
size: 15,
Icons.warning_rounded,
color: theme.colorScheme.onSecondaryContainer,
),
),
),
TextSpan(text: moduleDispute.title),
],
),
),
);
if (moduleDispute.jumpUrl?.isNotEmpty == true) {
return GestureDetector(
onTap: () => PageUtils.handleWebview(moduleDispute.jumpUrl!),
child: child,
);
}
return child;
}
}