mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-19 08:36:17 +08:00
* opt: cache * opt: MediaListPanel * feat: nested replyreply panel * tweaks * opt: abstract class * opt: PageStorageKey * opt: contextExt * opt: EpisodePanel * opt * opt: context instead GlobalKey * feat: jump to reply * refa: reply_reply * fix: jump * fix: index * update Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * opt: keepalive * reapply: nested replyreply * mod: spacing * opt: CommonSlidePageState * fix drag bottomsheet Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * opt reply jump Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * opt reply2reply Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * tweaks Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * tweaks Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * reapply: jumpToReply * fix: padding * fix: anim * fix some panels Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * opt: implements Scaffold * opt: remove keepalive * revert: GlobalKey * tweaks Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> --------- Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
326 lines
9.9 KiB
Dart
326 lines
9.9 KiB
Dart
import 'package:PiliPlus/common/constants.dart';
|
|
import 'package:PiliPlus/common/widgets/badge.dart';
|
|
import 'package:PiliPlus/common/widgets/dyn/ink_well.dart';
|
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
|
import 'package:PiliPlus/models/dynamics/result.dart';
|
|
import 'package:PiliPlus/pages/dynamics/widgets/forward_panel.dart';
|
|
import 'package:PiliPlus/pages/dynamics/widgets/live_panel.dart';
|
|
import 'package:PiliPlus/pages/dynamics/widgets/live_panel_sub.dart';
|
|
import 'package:PiliPlus/pages/dynamics/widgets/live_rcmd_panel.dart';
|
|
import 'package:PiliPlus/pages/dynamics/widgets/video_panel.dart';
|
|
import 'package:PiliPlus/utils/extension.dart';
|
|
import 'package:PiliPlus/utils/page_utils.dart';
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:flutter/material.dart' hide InkWell;
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
|
|
Widget noneWidget(ThemeData theme, String? tips) => Row(
|
|
spacing: 5,
|
|
children: [
|
|
Icon(
|
|
Icons.error,
|
|
size: 18,
|
|
color: theme.colorScheme.outline,
|
|
),
|
|
Text(
|
|
tips ?? '已失效',
|
|
style: TextStyle(color: theme.colorScheme.outline),
|
|
),
|
|
],
|
|
);
|
|
|
|
Widget module(
|
|
BuildContext context, {
|
|
required int floor,
|
|
required ThemeData theme,
|
|
required DynamicItemModel item,
|
|
required bool isSave,
|
|
required bool isDetail,
|
|
required double maxWidth,
|
|
}) {
|
|
final moduleDynamic = item.modules.moduleDynamic;
|
|
final major = moduleDynamic?.major;
|
|
|
|
if (major?.type == 'MAJOR_TYPE_NONE') {
|
|
return noneWidget(theme, major?.none?.tips);
|
|
}
|
|
|
|
switch (item.type) {
|
|
case 'DYNAMIC_TYPE_NONE':
|
|
return Row(
|
|
spacing: 4,
|
|
children: [
|
|
const Icon(FontAwesomeIcons.ghost, size: 14),
|
|
Text(major!.none!.tips!),
|
|
],
|
|
);
|
|
// 图文
|
|
case 'DYNAMIC_TYPE_DRAW':
|
|
// 文章
|
|
case 'DYNAMIC_TYPE_ARTICLE':
|
|
case 'DYNAMIC_TYPE_WORD':
|
|
return const SizedBox.shrink();
|
|
// 视频
|
|
case 'DYNAMIC_TYPE_AV':
|
|
case 'DYNAMIC_TYPE_UGC_SEASON':
|
|
case 'DYNAMIC_TYPE_PGC':
|
|
case 'DYNAMIC_TYPE_PGC_UNION':
|
|
case 'DYNAMIC_TYPE_COURSES_SEASON':
|
|
return videoSeasonWidget(
|
|
context,
|
|
theme: theme,
|
|
item: item,
|
|
floor: floor,
|
|
isSave: isSave,
|
|
isDetail: isDetail,
|
|
maxWidth: maxWidth,
|
|
);
|
|
// 转发
|
|
case 'DYNAMIC_TYPE_FORWARD':
|
|
return forwardPanel(
|
|
context,
|
|
theme: theme,
|
|
isSave: isSave,
|
|
orig: item.orig!,
|
|
isDetail: isDetail,
|
|
floor: floor + 1,
|
|
maxWidth: maxWidth,
|
|
);
|
|
// 直播
|
|
case 'DYNAMIC_TYPE_LIVE_RCMD':
|
|
return liveRcmdPanel(
|
|
context,
|
|
theme: theme,
|
|
isDetail: isDetail,
|
|
item: item,
|
|
floor: floor,
|
|
maxWidth: maxWidth,
|
|
);
|
|
// 直播
|
|
case 'DYNAMIC_TYPE_LIVE':
|
|
return livePanel(
|
|
context,
|
|
theme: theme,
|
|
item: item,
|
|
floor: floor,
|
|
isDetail: isDetail,
|
|
maxWidth: maxWidth,
|
|
);
|
|
// 活动
|
|
case 'DYNAMIC_TYPE_COMMON_SQUARE':
|
|
return Material(
|
|
color: floor == 1
|
|
? theme.dividerColor.withValues(alpha: 0.08)
|
|
: theme.colorScheme.surface,
|
|
shape: floor == 1
|
|
? null
|
|
: const RoundedRectangleBorder(borderRadius: StyleString.mdRadius),
|
|
child: InkWell(
|
|
borderRadius: floor == 1 ? null : StyleString.mdRadius,
|
|
onTap: () {
|
|
try {
|
|
String url = major.common!.jumpUrl!;
|
|
if (url.contains('bangumi/play') &&
|
|
PageUtils.viewPgcFromUri(url)) {
|
|
return;
|
|
}
|
|
PageUtils.handleWebview(url, inApp: true);
|
|
} catch (_) {}
|
|
},
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(
|
|
left: 12,
|
|
top: 10,
|
|
right: 12,
|
|
bottom: 10,
|
|
),
|
|
child: Row(
|
|
spacing: 10,
|
|
children: [
|
|
if (item
|
|
.modules
|
|
.moduleDynamic!
|
|
.major!
|
|
.common!
|
|
.cover
|
|
?.isNotEmpty ==
|
|
true)
|
|
ClipRRect(
|
|
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
|
child: CachedNetworkImage(
|
|
width: 45,
|
|
height: 45,
|
|
fit: BoxFit.cover,
|
|
imageUrl: item
|
|
.modules
|
|
.moduleDynamic!
|
|
.major!
|
|
.common!
|
|
.cover!
|
|
.http2https,
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Column(
|
|
spacing: 2,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
major!.common!.title!,
|
|
style: TextStyle(color: theme.colorScheme.primary),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
if (item
|
|
.modules
|
|
.moduleDynamic!
|
|
.major!
|
|
.common!
|
|
.desc
|
|
?.isNotEmpty ==
|
|
true)
|
|
Text(
|
|
major.common!.desc!,
|
|
style: TextStyle(
|
|
color: theme.colorScheme.outline,
|
|
fontSize: theme.textTheme.labelMedium!.fontSize,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
case 'DYNAMIC_TYPE_MUSIC':
|
|
final Map music = major!.music!;
|
|
return InkWell(
|
|
onTap: () => PageUtils.handleWebview("https:${music['jump_url']}"),
|
|
child: Container(
|
|
padding: const EdgeInsets.only(
|
|
left: 12,
|
|
top: 10,
|
|
right: 12,
|
|
bottom: 10,
|
|
),
|
|
color: theme.dividerColor.withValues(alpha: 0.08),
|
|
child: Row(
|
|
children: [
|
|
NetworkImgLayer(
|
|
radius: 8,
|
|
width: 45,
|
|
height: 45,
|
|
src: music['cover'],
|
|
),
|
|
const SizedBox(width: 10),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
music['title'],
|
|
style: TextStyle(
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
music['label'],
|
|
style: TextStyle(
|
|
color: theme.colorScheme.outline,
|
|
fontSize: theme.textTheme.labelMedium!.fontSize,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
case 'DYNAMIC_TYPE_MEDIALIST':
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (floor == 1) const SizedBox(width: 12),
|
|
if (major?.medialist?.cover?.isNotEmpty == true) ...[
|
|
Stack(
|
|
clipBehavior: Clip.none,
|
|
children: [
|
|
Hero(
|
|
tag: major!.medialist!.cover!,
|
|
child: NetworkImgLayer(
|
|
width: 180,
|
|
height: 110,
|
|
src: major.medialist!.cover,
|
|
),
|
|
),
|
|
PBadge(
|
|
right: 6,
|
|
top: 6,
|
|
text: major.medialist!.badge?.text,
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(width: 14),
|
|
],
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 110,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
major!.medialist!.title!,
|
|
style: TextStyle(
|
|
fontSize: theme.textTheme.titleMedium!.fontSize,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
if (major.medialist?.subTitle != null) ...[
|
|
const Spacer(),
|
|
Text(
|
|
major.medialist!.subTitle!,
|
|
style: TextStyle(
|
|
fontSize: theme.textTheme.labelLarge!.fontSize,
|
|
color: theme.colorScheme.outline,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
),
|
|
if (floor == 1) const SizedBox(width: 12),
|
|
],
|
|
);
|
|
|
|
case 'DYNAMIC_TYPE_SUBSCRIPTION_NEW'
|
|
when major?.type == 'MAJOR_TYPE_SUBSCRIPTION_NEW':
|
|
return livePanelSub(
|
|
context,
|
|
theme: theme,
|
|
isDetail: isDetail,
|
|
item: item,
|
|
floor: floor,
|
|
maxWidth: maxWidth,
|
|
);
|
|
|
|
default:
|
|
return Padding(
|
|
padding: floor == 1
|
|
? const EdgeInsets.symmetric(horizontal: 12)
|
|
: EdgeInsets.zero,
|
|
child: Text('暂未支持的类型: \n${item.idStr}\n${item.type}'),
|
|
);
|
|
}
|
|
}
|