opt dyn panel

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-09-03 13:29:16 +08:00
parent a635767561
commit 04830c7789
22 changed files with 1353 additions and 1426 deletions

View File

@@ -52,9 +52,9 @@ class RetryInterceptor extends Interceptor {
case DioExceptionType.sendTimeout:
case DioExceptionType.unknown:
if ((err.requestOptions.extra['_rt'] ??= 0) < _count &&
err.error
is! TransportConnectionException // 网络中断, 此时请求可能已经被服务器所接收
) {
err.error
is! TransportConnectionException // 网络中断, 此时请求可能已经被服务器所接收
) {
Future.delayed(
Duration(
milliseconds: ++err.requestOptions.extra['_rt'] * _delay,

View File

@@ -64,7 +64,7 @@ class DynamicsDataModel {
continue;
}
if (enableFilter) {
if (item.orig case DynamicItemModel orig) {
if (item.orig case final orig?) {
if (banWordForDyn.hasMatch(_getMatchText(orig))) {
continue;
}

View File

@@ -240,7 +240,7 @@ class OpusContent extends StatelessWidget {
if (item.word != null) {
return _getSpan(item.word);
}
if (item.rich case Rich rich) {
if (item.rich case final rich?) {
final hasUrl = rich.jumpUrl?.isNotEmpty == true;
return TextSpan(
text: '${hasUrl ? '\u{1F517}' : ''}${rich.text}',
@@ -633,6 +633,8 @@ Widget moduleBlockedItem(
ModuleBlocked moduleBlocked,
double maxWidth,
) {
late final isDarkMode = Get.isDarkMode;
BoxDecoration? bgImg() {
return moduleBlocked.bgImg == null
? null
@@ -641,7 +643,7 @@ Widget moduleBlockedItem(
fit: BoxFit.fill,
image: CachedNetworkImageProvider(
ImageUtil.thumbnailUrl(
Get.isDarkMode
isDarkMode
? moduleBlocked.bgImg!.imgDark
: moduleBlocked.bgImg!.imgDay,
),
@@ -655,9 +657,7 @@ Widget moduleBlockedItem(
width: width,
fit: BoxFit.contain,
imageUrl: ImageUtil.thumbnailUrl(
Get.isDarkMode
? moduleBlocked.icon!.imgDark
: moduleBlocked.icon!.imgDay,
isDarkMode ? moduleBlocked.icon!.imgDark : moduleBlocked.icon!.imgDay,
),
);
}
@@ -672,7 +672,7 @@ Widget moduleBlockedItem(
padding: padding,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: visualDensity,
backgroundColor: Get.isDarkMode
backgroundColor: isDarkMode
? const Color(0xFF8F0030)
: const Color(0xFFFF6699),
foregroundColor: Colors.white,

View File

@@ -61,10 +61,8 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
@override
void dispose() {
if (hasPub) {
for (var i in pathList) {
File(i).delSync();
}
for (var i in pathList) {
File(i).delSync();
}
super.dispose();
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,9 +26,9 @@ import 'package:get/get.dart' hide ContextExtensionss;
class AuthorPanel extends StatelessWidget {
final DynamicItemModel item;
final Function? addBannedList;
final bool isSave;
final bool isDetail;
final ValueChanged? onRemove;
final bool isSave;
final Function(bool isTop, dynamic dynId)? onSetTop;
final VoidCallback? onBlock;
@@ -43,10 +43,10 @@ class AuthorPanel extends StatelessWidget {
this.onBlock,
});
Widget _buildAvatar() {
String? pendant = item.modules.moduleAuthor?.pendant?.image;
Widget _buildAvatar(ModuleAuthorModel moduleAuthor) {
String? pendant = moduleAuthor.pendant?.image;
Widget avatar = PendantAvatar(
avatar: item.modules.moduleAuthor?.face,
avatar: moduleAuthor.face,
size: pendant.isNullOrEmpty ? 40 : 34,
officialType: null, // 已被注释
garbPendantImage: pendant,
@@ -60,14 +60,15 @@ class AuthorPanel extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final pubTime = item.modules.moduleAuthor?.pubTs != null
final moduleAuthor = item.modules.moduleAuthor!;
final pubTime = moduleAuthor.pubTs != null
? isSave
? DateUtil.format(
item.modules.moduleAuthor!.pubTs,
moduleAuthor.pubTs,
format: DateUtil.longFormatDs,
)
: DateUtil.dateFormat(item.modules.moduleAuthor!.pubTs)
: item.modules.moduleAuthor?.pubTime;
: DateUtil.dateFormat(moduleAuthor.pubTs)
: moduleAuthor.pubTime;
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
@@ -76,29 +77,29 @@ class AuthorPanel extends StatelessWidget {
alignment: Alignment.centerLeft,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: item.modules.moduleAuthor!.type == 'AUTHOR_TYPE_NORMAL'
onTap: moduleAuthor.type == 'AUTHOR_TYPE_NORMAL'
? () {
feedBack();
Get.toNamed(
'/member?mid=${item.modules.moduleAuthor!.mid}',
'/member?mid=${moduleAuthor.mid}',
);
}
: null,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildAvatar(),
_buildAvatar(moduleAuthor),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.modules.moduleAuthor?.name ?? '',
moduleAuthor.name ?? '',
style: TextStyle(
color:
item.modules.moduleAuthor!.vip != null &&
item.modules.moduleAuthor!.vip!.status > 0 &&
item.modules.moduleAuthor!.vip!.type == 2
moduleAuthor.vip != null &&
moduleAuthor.vip!.status > 0 &&
moduleAuthor.vip!.type == 2
? theme.colorScheme.vipColor
: theme.colorScheme.onSurface,
fontSize: theme.textTheme.titleSmall!.fontSize,
@@ -106,7 +107,7 @@ class AuthorPanel extends StatelessWidget {
),
if (pubTime != null)
Text(
'$pubTime${item.modules.moduleAuthor?.pubAction != null ? ' ${item.modules.moduleAuthor!.pubAction}' : ''}',
'$pubTime${moduleAuthor.pubAction != null ? ' ${moduleAuthor.pubAction}' : ''}',
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelSmall!.fontSize,
@@ -155,7 +156,7 @@ class AuthorPanel extends StatelessWidget {
_moreWidget(context),
],
)
: item.modules.moduleAuthor!.decorate != null
: moduleAuthor.decorate != null
? Row(
mainAxisSize: MainAxisSize.min,
children: [
@@ -165,48 +166,24 @@ class AuthorPanel extends StatelessWidget {
children: [
CachedNetworkImage(
height: 32,
imageUrl: item
.modules
.moduleAuthor!
.decorate!
.cardUrl
.http2https,
imageUrl: moduleAuthor.decorate!.cardUrl.http2https,
),
if (item
.modules
.moduleAuthor
?.decorate
?.fan
?.numStr
?.isNotEmpty ==
if (moduleAuthor.decorate?.fan?.numStr?.isNotEmpty ==
true)
Padding(
padding: const EdgeInsets.only(right: 32),
child: Text(
'${item.modules.moduleAuthor!.decorate!.fan!.numStr}',
'${moduleAuthor.decorate!.fan!.numStr}',
style: TextStyle(
height: 1,
fontSize: 11,
fontFamily: 'digital_id_num',
color:
item
.modules
.moduleAuthor!
.decorate!
.fan
?.color
moduleAuthor.decorate!.fan?.color
?.startsWith('#') ==
true
? Color(
int.parse(
item
.modules
.moduleAuthor!
.decorate!
.fan!
.color!
.replaceFirst('#', '0xFF'),
),
? Utils.parseColor(
moduleAuthor.decorate!.fan!.color!,
)
: null,
),
@@ -250,7 +227,7 @@ class AuthorPanel extends StatelessWidget {
if (bvid == null && item.orig != null) {
bvid = getBvid(
item.orig!.type,
item.orig?.modules.moduleDynamic?.major,
item.orig!.modules.moduleDynamic?.major,
);
}
} catch (_) {}
@@ -264,6 +241,7 @@ class AuthorPanel extends StatelessWidget {
),
builder: (context1) {
final theme = Theme.of(context);
final moduleAuthor = item.modules.moduleAuthor!;
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.viewPaddingOf(context1).bottom,
@@ -347,32 +325,13 @@ class AuthorPanel extends StatelessWidget {
bool isDyn = item.basic!.commentType == 17;
String id = isDyn ? item.idStr : item.basic!.ridStr!;
int source = isDyn ? 11 : 2;
String title;
if (item.modules.moduleDynamic?.desc != null) {
title = item.modules.moduleDynamic!.desc!.text!;
} else if (item.modules.moduleDynamic?.major != null) {
title = item
.modules
.moduleDynamic!
.major!
.opus!
.summary!
.text!;
} else {
throw UnsupportedError(
'error getting title: {"type": ${item.basic!.commentType}, "id": $id}',
);
}
final moduleDynamic = item.modules.moduleDynamic!;
final title =
moduleDynamic.desc?.text ??
moduleDynamic.major!.opus!.summary!.text!;
String? thumb = isDyn
? item.modules.moduleAuthor?.face
: item
.modules
.moduleDynamic
?.major
?.opus
?.pics
?.firstOrNull
?.url;
? moduleAuthor.face
: moduleDynamic.major?.opus?.pics?.firstOrNull?.url;
PageUtils.pmShare(
context,
content: {
@@ -381,9 +340,8 @@ class AuthorPanel extends StatelessWidget {
"headline": "",
"source": source,
if (thumb?.isNotEmpty == true) "thumb": thumb,
"author": item.modules.moduleAuthor!.name,
"author_id": item.modules.moduleAuthor!.mid
.toString(),
"author": moduleAuthor.name,
"author_id": moduleAuthor.mid.toString(),
},
);
} catch (e) {
@@ -394,7 +352,7 @@ class AuthorPanel extends StatelessWidget {
),
ListTile(
title: Text(
'临时屏蔽:${item.modules.moduleAuthor?.name}',
'临时屏蔽:${moduleAuthor.name}',
style: theme.textTheme.titleSmall,
),
leading: const Icon(Icons.visibility_off_outlined, size: 19),
@@ -403,16 +361,16 @@ class AuthorPanel extends StatelessWidget {
onBlock?.call();
try {
Get.find<DynamicsController>().tempBannedList.add(
item.modules.moduleAuthor!.mid!,
moduleAuthor.mid!,
);
SmartDialog.showToast(
'已临时屏蔽${item.modules.moduleAuthor?.name}(${item.modules.moduleAuthor!.mid!}),重启恢复',
'已临时屏蔽${moduleAuthor.name}(${moduleAuthor.mid!}),重启恢复',
);
} catch (_) {}
},
minLeadingWidth: 0,
),
if (item.modules.moduleAuthor?.mid == Accounts.main.mid) ...[
if (moduleAuthor.mid == Accounts.main.mid) ...[
ListTile(
onTap: () {
Get.back();
@@ -512,13 +470,13 @@ class AuthorPanel extends StatelessWidget {
(reasonType, reasonDesc, banUid) {
if (banUid) {
VideoHttp.relationMod(
mid: item.modules.moduleAuthor!.mid!,
mid: moduleAuthor.mid!,
act: 5,
reSrc: 11,
);
}
return UserHttp.dynamicReport(
mid: item.modules.moduleAuthor!.mid,
mid: moduleAuthor.mid,
dynId: item.idStr,
reasonType: reasonType,
);

View File

@@ -3,13 +3,13 @@ import 'package:PiliPlus/pages/article/widgets/opus_content.dart'
show moduleBlockedItem;
import 'package:flutter/material.dart';
Widget blockedItem(
ThemeData theme,
ModuleBlocked moduleBlocked, {
Widget blockedItem({
required ThemeData theme,
required ModuleBlocked blocked,
required double maxWidth,
}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 1),
child: moduleBlockedItem(theme, moduleBlocked, maxWidth - 26),
child: moduleBlockedItem(theme, blocked, maxWidth - 26),
);
}

View File

@@ -8,19 +8,26 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
Widget content(
ThemeData theme,
bool isSave,
BuildContext context,
DynamicItemModel item,
bool isDetail,
Function(List<String>, int)? callback, {
floor = 1,
BuildContext context, {
required int floor,
required ThemeData theme,
required DynamicItemModel item,
required bool isSave,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
if (floor == 1) {
maxWidth -= 24;
}
TextSpan? richNodes = richNode(theme, item, context, maxWidth: maxWidth);
TextSpan? richNodes = richNode(
context,
theme: theme,
item: item,
maxWidth: maxWidth,
);
final moduleDynamic = item.modules.moduleDynamic;
final pics = moduleDynamic?.major?.opus?.pics;
return Padding(
padding: floor == 1
? const EdgeInsets.fromLTRB(12, 0, 12, 6)
@@ -28,13 +35,13 @@ Widget content(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (item.modules.moduleDynamic?.topic != null)
if (moduleDynamic?.topic case final topic?)
GestureDetector(
onTap: () => Get.toNamed(
'/dynTopic',
parameters: {
'id': item.modules.moduleDynamic!.topic!.id!.toString(),
'name': item.modules.moduleDynamic!.topic!.name!,
'id': topic.id!.toString(),
'name': topic.name!,
},
),
child: Text.rich(
@@ -51,7 +58,7 @@ Widget content(
),
),
),
TextSpan(text: item.modules.moduleDynamic!.topic!.name),
TextSpan(text: topic.name),
],
),
style: TextStyle(
@@ -79,10 +86,10 @@ Widget content(
richNodes,
maxLines: isSave ? null : 6,
),
if (item.modules.moduleDynamic?.major?.opus?.pics?.isNotEmpty == true)
if (pics?.isNotEmpty == true)
CustomGridView(
maxWidth: maxWidth,
picArr: item.modules.moduleDynamic!.major!.opus!.pics!
picArr: pics!
.map(
(item) => ImageModel(
width: item.width,

View File

@@ -0,0 +1,52 @@
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/additional_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/blocked_item.dart';
import 'package:PiliPlus/pages/dynamics/widgets/content_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/module_panel.dart';
import 'package:flutter/material.dart';
List<Widget> dynContent(
BuildContext context, {
required int floor,
required ThemeData theme,
required DynamicItemModel item,
required bool isSave,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
final moduleDynamic = item.modules.moduleDynamic;
return [
if (item.type != 'DYNAMIC_TYPE_NONE')
content(
context,
theme: theme,
isSave: isSave,
isDetail: isDetail,
item: item,
floor: floor,
callback: callback,
maxWidth: maxWidth,
),
module(
context,
theme: theme,
isSave: isSave,
isDetail: isDetail,
item: item,
floor: floor,
callback: callback,
maxWidth: maxWidth,
),
if (moduleDynamic?.additional case final additional?)
addWidget(
theme: theme,
context,
idStr: item.idStr,
additional: additional,
floor: floor,
),
if (moduleDynamic?.major?.blocked case final blocked?)
blockedItem(theme: theme, blocked: blocked, maxWidth: maxWidth),
];
}

View File

@@ -2,11 +2,8 @@ import 'package:PiliPlus/common/widgets/dyn/ink_well.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/action_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/additional_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/author_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/blocked_item.dart';
import 'package:PiliPlus/pages/dynamics/widgets/content_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/module_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/dyn_content.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart' hide InkWell;
@@ -76,37 +73,20 @@ class DynamicPanel extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(12, 12, 12, 6),
child: authorWidget,
),
if (item.type != 'DYNAMIC_TYPE_NONE')
content(
theme,
isSave,
context,
item,
isDetail,
callback,
maxWidth: maxWidth,
),
module(
theme,
isSave,
item,
...dynContent(
context,
isDetail,
callback,
theme: theme,
isSave: isSave,
isDetail: isDetail,
item: item,
floor: 1,
callback: callback,
maxWidth: maxWidth,
),
if (item.modules.moduleDynamic?.additional != null)
addWidget(theme, item, context),
if (item.modules.moduleDynamic?.major?.blocked != null)
blockedItem(
theme,
item.modules.moduleDynamic!.major!.blocked!,
maxWidth: maxWidth,
),
const SizedBox(height: 2),
if (!isDetail) ...[
ActionPanel(item: item),
if (item.modules.moduleFold case ModuleFold moduleFold) ...[
if (item.modules.moduleFold case final moduleFold?) ...[
Divider(
height: 1,
color: theme.dividerColor.withValues(alpha: 0.1),

View File

@@ -0,0 +1,135 @@
import 'package:PiliPlus/common/widgets/dyn/ink_well.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/dyn_content.dart';
import 'package:PiliPlus/pages/dynamics/widgets/module_panel.dart';
import 'package:PiliPlus/utils/date_util.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart' hide InkWell;
import 'package:get/get.dart';
Widget forwardPanel(
BuildContext context, {
required int floor,
required ThemeData theme,
required DynamicItemModel orig,
required bool isSave,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
final moduleDynamic = orig.modules.moduleDynamic;
final major = moduleDynamic?.major;
final isNoneMajor = major?.type == 'MAJOR_TYPE_NONE';
Widget child;
if (isNoneMajor) {
child = noneWidget(theme, major?.none?.tips);
} else {
child = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_forwardAuthor(
theme: theme,
moduleAuthor: orig.modules.moduleAuthor!,
isSave: isSave,
),
const SizedBox(height: 5),
...dynContent(
context,
theme: theme,
isSave: isSave,
isDetail: isDetail,
item: orig,
floor: floor + 1,
callback: callback,
maxWidth: maxWidth - 30,
),
],
);
}
child = Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
color: theme.dividerColor.withValues(alpha: 0.08),
child: child,
);
if (isNoneMajor) {
return child;
}
return InkWell(
onTap: () => PageUtils.pushDynDetail(orig),
onLongPress: () {
String? title, cover, bvid;
switch (orig.type) {
case 'DYNAMIC_TYPE_AV':
title = major?.archive?.title;
cover = major?.archive?.cover;
bvid = major?.archive?.bvid;
break;
case 'DYNAMIC_TYPE_UGC_SEASON':
title = major?.ugcSeason?.title;
cover = major?.ugcSeason?.cover;
bvid = major?.ugcSeason?.bvid;
break;
case 'DYNAMIC_TYPE_PGC' || 'DYNAMIC_TYPE_PGC_UNION':
title = major?.pgc?.title;
cover = major?.pgc?.cover;
break;
case 'DYNAMIC_TYPE_LIVE_RCMD':
title = major?.liveRcmd?.title;
cover = major?.liveRcmd?.cover;
break;
case 'DYNAMIC_TYPE_LIVE':
title = major?.live?.title;
cover = major?.live?.cover;
break;
default:
return;
}
if (cover != null) {
imageSaveDialog(
title: title,
cover: cover,
bvid: bvid,
);
}
},
child: child,
);
}
Widget _forwardAuthor({
required ThemeData theme,
required ModuleAuthorModel moduleAuthor,
required bool isSave,
}) {
final isNormalAuth = moduleAuthor.type == 'AUTHOR_TYPE_NORMAL';
return Row(
children: [
GestureDetector(
onTap: isNormalAuth
? () => Get.toNamed('/member?mid=${moduleAuthor.mid}')
: null,
child: Text(
'${isNormalAuth ? '@' : ''}${moduleAuthor.name}',
style: TextStyle(color: theme.colorScheme.primary),
),
),
const SizedBox(width: 6),
Text(
isSave
? DateUtil.format(moduleAuthor.pubTs, format: DateUtil.longFormatDs)
: DateUtil.dateFormat(moduleAuthor.pubTs),
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelSmall!.fontSize,
),
),
],
);
}

View File

@@ -1,73 +1,65 @@
import 'package:PiliPlus/common/widgets/image/image_save.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart';
Widget livePanel(
ThemeData theme,
bool isDetail,
DynamicItemModel item,
BuildContext context, {
int floor = 1,
required int floor,
required ThemeData theme,
required DynamicItemModel item,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
DynamicMajorModel? content = item.modules.moduleDynamic!.major;
if (content == null) {
DynamicLive2Model? live = item.modules.moduleDynamic!.major!.live;
if (live == null) {
return const SizedBox.shrink();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => PageUtils.toLiveRoom(content.live?.id),
onLongPress: () {
Feedback.forLongPress(context);
imageSaveDialog(
title: content.live!.title,
cover: content.live!.cover,
);
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
NetworkImgLayer(
width: 120,
height: 75,
src: content.live!.cover,
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
content.live!.title!,
maxLines: isDetail ? null : 2,
overflow: isDetail ? null : TextOverflow.ellipsis,
),
const SizedBox(height: 4),
if (content.live?.descFirst != null)
Text(
content.live!.descFirst!,
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelMedium!.fontSize,
),
),
],
),
),
if (content.live!.badge?.text != null)
Text(
content.live!.badge!.text!,
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
),
),
],
return Padding(
padding: floor == 1
? const EdgeInsets.symmetric(horizontal: 12)
: EdgeInsets.zero,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
NetworkImgLayer(
width: 120,
height: 75,
src: live.cover,
),
),
],
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
live.title!,
maxLines: isDetail ? null : 2,
overflow: isDetail ? null : TextOverflow.ellipsis,
),
const SizedBox(height: 4),
if (live.descFirst case final descFirst?)
Text(
descFirst,
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelMedium!.fontSize,
),
),
],
),
),
if (live.badge?.text case final badge?)
Text(
badge,
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: live.liveState == 1
? theme.colorScheme.primary
: theme.colorScheme.outline,
),
),
],
),
);
}

View File

@@ -3,117 +3,116 @@ import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/badge_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart';
Widget livePanelSub(
ThemeData theme,
bool isDetail,
DynamicItemModel item,
BuildContext context, {
int floor = 1,
required int floor,
required ThemeData theme,
required DynamicItemModel item,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
maxWidth -= 24;
SubscriptionNew? subItem = item.modules.moduleDynamic!.major?.subscriptionNew;
LivePlayInfo? content = subItem?.liveRcmd?.content?.livePlayInfo;
if (subItem == null || content == null) {
LivePlayInfo? live = item
.modules
.moduleDynamic!
.major
?.subscriptionNew
?.liveRcmd
?.content
?.livePlayInfo;
if (live == null) {
return const SizedBox.shrink();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
child: GestureDetector(
onTap: () => PageUtils.toLiveRoom(content.roomId),
child: Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
width: maxWidth,
height: maxWidth / StyleString.aspectRatio,
src: content.cover,
quality: 40,
),
PBadge(
text: content.watchedShow?.textLarge,
EdgeInsets padding;
if (floor == 1) {
maxWidth -= 24;
padding = const EdgeInsets.symmetric(horizontal: 12);
} else {
padding = EdgeInsets.zero;
}
return Padding(
padding: padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
width: maxWidth,
height: maxWidth / StyleString.aspectRatio,
src: live.cover,
quality: 40,
),
PBadge(
text: live.watchedShow?.textLarge,
top: 6,
right: 65,
fontSize: 10.5,
type: PBadgeType.gray,
),
if (live.liveStatus == 1)
Positioned(
right: 6,
top: 6,
right: 65,
fontSize: 10.5,
type: PBadgeType.gray,
child: Image.asset(
height: 16,
'assets/images/live/live.gif',
filterQuality: FilterQuality.low,
),
)
else
const PBadge(
text: '直播结束',
top: 6,
right: 6,
),
if (content.liveStatus == 1)
Positioned(
right: 6,
top: 6,
child: Image.asset(
height: 16,
'assets/images/live/live.gif',
filterQuality: FilterQuality.low,
),
)
else
const PBadge(
text: '直播结束',
top: 6,
right: 6,
),
if (content.areaName != null)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 80,
alignment: Alignment.bottomLeft,
padding: const EdgeInsets.fromLTRB(12, 0, 10, 10),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Colors.transparent,
Colors.black45,
],
),
borderRadius: floor == 1
? const BorderRadius.only(
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
)
: const BorderRadius.only(
bottomLeft: Radius.circular(6),
bottomRight: Radius.circular(6),
),
if (live.areaName case final areaName?)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 80,
alignment: Alignment.bottomLeft,
padding: const EdgeInsets.fromLTRB(12, 0, 10, 10),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Colors.transparent,
Colors.black45,
],
),
child: Text(
content.areaName!,
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: Colors.white,
),
borderRadius: BorderRadius.only(
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
),
),
child: Text(
areaName,
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: Colors.white,
),
),
),
],
),
),
],
),
),
const SizedBox(height: 6),
if (content.title != null)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace,
),
child: Text(
content.title!,
const SizedBox(height: 6),
if (live.title case final title?)
Text(
title,
maxLines: isDetail ? null : 1,
style: const TextStyle(fontWeight: FontWeight.bold),
overflow: isDetail ? null : TextOverflow.ellipsis,
),
),
const SizedBox(height: 2),
],
const SizedBox(height: 2),
],
),
);
}

View File

@@ -3,117 +3,110 @@ import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/badge_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart';
Widget liveRcmdPanel(
ThemeData theme,
bool isDetail,
DynamicItemModel item,
BuildContext context, {
int floor = 1,
required int floor,
required ThemeData theme,
required DynamicItemModel item,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
maxWidth -= 24;
DynamicLiveModel? liveRcmd = item.modules.moduleDynamic?.major?.liveRcmd;
if (liveRcmd == null) {
return const SizedBox.shrink();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
child: GestureDetector(
onTap: () => PageUtils.pushDynDetail(item),
child: Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
width: maxWidth,
height: maxWidth / StyleString.aspectRatio,
src: liveRcmd.cover,
quality: 40,
),
PBadge(
text: liveRcmd.watchedShow?.textLarge,
EdgeInsets padding;
if (floor == 1) {
maxWidth -= 24;
padding = const EdgeInsets.symmetric(horizontal: 12);
} else {
padding = EdgeInsets.zero;
}
return Padding(
padding: padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
width: maxWidth,
height: maxWidth / StyleString.aspectRatio,
src: liveRcmd.cover,
quality: 40,
),
PBadge(
text: liveRcmd.watchedShow?.textLarge,
top: 6,
right: 65,
fontSize: 10.5,
type: PBadgeType.gray,
),
if (liveRcmd.liveStatus == 1)
Positioned(
right: 6,
top: 6,
right: 65,
fontSize: 10.5,
child: Image.asset(
height: 16,
'assets/images/live/live.gif',
filterQuality: FilterQuality.low,
),
)
else
const PBadge(
text: '直播结束',
top: 6,
right: 6,
type: PBadgeType.gray,
),
if (liveRcmd.liveStatus == 1)
Positioned(
right: 6,
top: 6,
child: Image.asset(
height: 16,
'assets/images/live/live.gif',
filterQuality: FilterQuality.low,
),
)
else
const PBadge(
text: '直播结束',
top: 6,
right: 6,
type: PBadgeType.gray,
),
if (liveRcmd.areaName != null)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 80,
alignment: Alignment.bottomLeft,
padding: const EdgeInsets.fromLTRB(12, 0, 10, 10),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Colors.transparent,
Colors.black45,
],
),
borderRadius: floor == 1
? const BorderRadius.only(
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
)
: const BorderRadius.only(
bottomLeft: Radius.circular(6),
bottomRight: Radius.circular(6),
),
if (liveRcmd.areaName case final areaName?)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 80,
alignment: Alignment.bottomLeft,
padding: const EdgeInsets.fromLTRB(12, 0, 10, 10),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Colors.transparent,
Colors.black45,
],
),
child: Text(
liveRcmd.areaName!,
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: Colors.white,
),
borderRadius: BorderRadius.only(
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
),
),
child: Text(
areaName,
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: Colors.white,
),
),
),
],
),
),
],
),
),
const SizedBox(height: 6),
if (liveRcmd.title != null)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace,
),
child: Text(
liveRcmd.title!,
const SizedBox(height: 6),
if (liveRcmd.title case final title?)
Text(
title,
maxLines: isDetail ? null : 1,
style: const TextStyle(fontWeight: FontWeight.bold),
overflow: isDetail ? null : TextOverflow.ellipsis,
),
),
const SizedBox(height: 2),
],
const SizedBox(height: 2),
],
),
);
}

View File

@@ -1,36 +1,60 @@
// 转发
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/image_save.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/additional_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/blocked_item.dart';
import 'package:PiliPlus/pages/dynamics/widgets/content_panel.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/date_util.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';
import 'package:get/get.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(
ThemeData theme,
bool isSave,
DynamicItemModel item,
BuildContext context,
bool isDetail,
Function(List<String>, int)? callback, {
floor = 1,
BuildContext context, {
required int floor,
required ThemeData theme,
required DynamicItemModel item,
required bool isSave,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
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':
// 文章
@@ -44,173 +68,46 @@ Widget module(
case 'DYNAMIC_TYPE_PGC_UNION':
case 'DYNAMIC_TYPE_COURSES_SEASON':
return videoSeasonWidget(
theme,
isSave,
isDetail,
item,
context,
callback,
theme: theme,
item: item,
floor: floor,
isSave: isSave,
isDetail: isDetail,
callback: callback,
maxWidth: maxWidth,
);
// 转发
case 'DYNAMIC_TYPE_FORWARD':
final orig = item.orig!;
final isNoneMajor =
orig.modules.moduleDynamic?.major?.type == 'MAJOR_TYPE_NONE';
late final isNormalAuth =
orig.modules.moduleAuthor!.type == 'AUTHOR_TYPE_NORMAL';
if (isNoneMajor) {
if (orig.modules.moduleDynamic?.major?.none?.tips?.isNotEmpty == true) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
color: theme.dividerColor.withValues(alpha: 0.08),
child: Row(
children: [
Icon(
Icons.error,
size: 18,
color: theme.colorScheme.outline,
),
const SizedBox(width: 5),
Text(
orig.modules.moduleDynamic!.major!.none!.tips!,
style: TextStyle(color: theme.colorScheme.outline),
),
],
),
);
} else {
return const SizedBox.shrink();
}
}
maxWidth -= 30;
return InkWell(
onTap: () => PageUtils.pushDynDetail(orig),
onLongPress: () {
String? title, cover, bvid;
late var origMajor = orig.modules.moduleDynamic?.major;
late var major = item.modules.moduleDynamic?.major;
switch (orig.type) {
case 'DYNAMIC_TYPE_AV':
title = origMajor?.archive?.title;
cover = origMajor?.archive?.cover;
bvid = origMajor?.archive?.bvid;
break;
case 'DYNAMIC_TYPE_UGC_SEASON':
title = origMajor?.ugcSeason?.title;
cover = origMajor?.ugcSeason?.cover;
bvid = origMajor?.ugcSeason?.bvid;
break;
case 'DYNAMIC_TYPE_PGC' || 'DYNAMIC_TYPE_PGC_UNION':
title = origMajor?.pgc?.title;
cover = origMajor?.pgc?.cover;
break;
case 'DYNAMIC_TYPE_LIVE_RCMD':
title = major?.liveRcmd?.title;
cover = major?.liveRcmd?.cover;
break;
case 'DYNAMIC_TYPE_LIVE':
title = major?.live?.title;
cover = major?.live?.cover;
break;
default:
return;
}
imageSaveDialog(
title: title,
cover: cover,
bvid: bvid,
);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
color: theme.dividerColor.withValues(alpha: 0.08),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
GestureDetector(
onTap: isNormalAuth
? () => Get.toNamed(
'/member?mid=${orig.modules.moduleAuthor!.mid}',
)
: null,
child: Text(
'${isNormalAuth ? '@' : ''}${orig.modules.moduleAuthor!.name}',
style: TextStyle(color: theme.colorScheme.primary),
),
),
const SizedBox(width: 6),
Text(
isSave
? DateUtil.format(
orig.modules.moduleAuthor!.pubTs,
format: DateUtil.longFormatDs,
)
: DateUtil.dateFormat(orig.modules.moduleAuthor!.pubTs),
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelSmall!.fontSize,
),
),
],
),
const SizedBox(height: 5),
content(
theme,
isSave,
context,
orig,
isDetail,
callback,
floor: floor + 1,
maxWidth: maxWidth,
),
module(
theme,
isSave,
orig,
context,
isDetail,
callback,
floor: floor + 1,
maxWidth: maxWidth,
),
if (orig.modules.moduleDynamic?.additional != null)
addWidget(theme, orig, context, floor: floor + 1),
if (orig.modules.moduleDynamic?.major?.blocked != null)
blockedItem(
theme,
orig.modules.moduleDynamic!.major!.blocked!,
maxWidth: maxWidth,
),
],
),
),
return forwardPanel(
context,
theme: theme,
isSave: isSave,
orig: item.orig!,
isDetail: isDetail,
callback: callback,
floor: floor + 1,
maxWidth: maxWidth,
);
// 直播
case 'DYNAMIC_TYPE_LIVE_RCMD':
return liveRcmdPanel(
theme,
isDetail,
item,
context,
theme: theme,
isDetail: isDetail,
item: item,
floor: floor,
maxWidth: maxWidth,
);
// 直播
case 'DYNAMIC_TYPE_LIVE':
return livePanel(theme, isDetail, item, context, floor: floor);
case 'DYNAMIC_TYPE_NONE':
return Row(
spacing: 4,
children: [
const Icon(FontAwesomeIcons.ghost, size: 14),
Text(item.modules.moduleDynamic!.major!.none!.tips!),
],
return livePanel(
context,
theme: theme,
item: item,
floor: floor,
isDetail: isDetail,
maxWidth: maxWidth,
);
// 活动
case 'DYNAMIC_TYPE_COMMON_SQUARE':
@@ -225,7 +122,7 @@ Widget module(
borderRadius: floor == 1 ? null : StyleString.mdRadius,
onTap: () {
try {
String url = item.modules.moduleDynamic!.major!.common!.jumpUrl!;
String url = major.common!.jumpUrl!;
if (url.contains('bangumi/play') &&
PageUtils.viewPgcFromUri(url)) {
return;
@@ -272,7 +169,7 @@ Widget module(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.modules.moduleDynamic!.major!.common!.title!,
major!.common!.title!,
style: TextStyle(color: theme.colorScheme.primary),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -286,7 +183,7 @@ Widget module(
?.isNotEmpty ==
true)
Text(
item.modules.moduleDynamic!.major!.common!.desc!,
major.common!.desc!,
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelMedium!.fontSize,
@@ -303,7 +200,7 @@ Widget module(
),
);
case 'DYNAMIC_TYPE_MUSIC':
final Map music = item.modules.moduleDynamic!.major!.music!;
final Map music = major!.music!;
return InkWell(
onTap: () => PageUtils.handleWebview("https:${music['jump_url']}"),
child: Container(
@@ -355,24 +252,22 @@ Widget module(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (floor == 1) const SizedBox(width: 12),
if (item.modules.moduleDynamic!.major!.medialist!.cover?.isNotEmpty ==
true) ...[
if (major?.medialist?.cover?.isNotEmpty == true) ...[
Stack(
clipBehavior: Clip.none,
children: [
Hero(
tag: item.modules.moduleDynamic!.major!.medialist!.cover!,
tag: major!.medialist!.cover!,
child: NetworkImgLayer(
width: 180,
height: 110,
src: item.modules.moduleDynamic!.major!.medialist!.cover,
src: major.medialist!.cover,
),
),
PBadge(
right: 6,
top: 6,
text:
item.modules.moduleDynamic!.major!.medialist!.badge?.text,
text: major.medialist!.badge?.text,
),
],
),
@@ -387,17 +282,16 @@ Widget module(
children: [
const SizedBox(height: 4),
Text(
item.modules.moduleDynamic!.major!.medialist!.title!,
major!.medialist!.title!,
style: TextStyle(
fontSize: theme.textTheme.titleMedium!.fontSize,
fontWeight: FontWeight.bold,
),
),
if (item.modules.moduleDynamic?.major?.medialist?.subTitle !=
null) ...[
if (major.medialist?.subTitle != null) ...[
const Spacer(),
Text(
item.modules.moduleDynamic!.major!.medialist!.subTitle!,
major.medialist!.subTitle!,
style: TextStyle(
fontSize: theme.textTheme.labelLarge!.fontSize,
color: theme.colorScheme.outline,
@@ -413,14 +307,14 @@ Widget module(
);
case 'DYNAMIC_TYPE_SUBSCRIPTION_NEW'
when item.modules.moduleDynamic?.major?.type ==
'MAJOR_TYPE_SUBSCRIPTION_NEW':
when major?.type == 'MAJOR_TYPE_SUBSCRIPTION_NEW':
return livePanelSub(
theme,
isDetail,
item,
context,
theme: theme,
isDetail: isDetail,
item: item,
floor: floor,
callback: callback,
maxWidth: maxWidth,
);

View File

@@ -19,31 +19,32 @@ import 'package:get/get.dart';
// 富文本
TextSpan? richNode(
ThemeData theme,
DynamicItemModel item,
BuildContext context, {
required ThemeData theme,
required DynamicItemModel item,
required double maxWidth,
}) {
try {
late final style = TextStyle(color: theme.colorScheme.primary);
List<InlineSpan> spanChildren = [];
final moduleDynamic = item.modules.moduleDynamic;
List<RichTextNodeItem>? richTextNodes;
if (item.modules.moduleDynamic?.desc != null) {
richTextNodes = item.modules.moduleDynamic!.desc!.richTextNodes;
} else if (item.modules.moduleDynamic?.major != null) {
if (moduleDynamic?.desc case final desc?) {
richTextNodes = desc.richTextNodes;
} else if (moduleDynamic?.major?.opus case final opus?) {
// 动态页面 richTextNodes 层级可能与主页动态层级不同
richTextNodes =
item.modules.moduleDynamic!.major!.opus?.summary?.richTextNodes;
if (item.modules.moduleDynamic?.major?.opus?.title != null) {
richTextNodes = opus.summary?.richTextNodes;
if (opus.title case final title?) {
spanChildren.add(
TextSpan(
text: '${item.modules.moduleDynamic!.major!.opus!.title!}\n',
text: '$title\n',
style: const TextStyle(fontWeight: FontWeight.bold),
),
);
}
}
if (richTextNodes == null || richTextNodes.isEmpty) {
return null;
} else {

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/widgets/dyn/ink_well.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
import 'package:PiliPlus/models/common/image_type.dart';
@@ -6,7 +7,7 @@ import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/live_follow/view.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' hide InkWell;
import 'package:get/get.dart';
class UpPanel extends StatefulWidget {

View File

@@ -8,43 +8,20 @@ import 'package:PiliPlus/utils/num_util.dart';
import 'package:flutter/material.dart';
Widget videoSeasonWidget(
ThemeData theme,
bool isSave,
bool isDetail,
DynamicItemModel item,
BuildContext context,
Function(List<String>, int)? callback, {
floor = 1,
BuildContext context, {
required int floor,
required ThemeData theme,
required DynamicItemModel item,
required bool isSave,
required bool isDetail,
required double maxWidth,
Function(List<String>, int)? callback,
}) {
if (item.modules.moduleDynamic?.major?.type == 'MAJOR_TYPE_NONE') {
return item.modules.moduleDynamic?.major?.none?.tips != null
? Row(
children: [
Icon(
Icons.error,
size: 18,
color: theme.colorScheme.outline,
),
const SizedBox(width: 5),
Text(
item.modules.moduleDynamic!.major!.none!.tips!,
style: TextStyle(color: theme.colorScheme.outline),
),
],
)
: const SizedBox.shrink();
}
// type archive ugcSeason
// archive 视频/显示发布人
// ugcSeason 合集/不显示发布人
// floor 1 2
// 1 投稿视频 铺满 borderRadius 0
// 2 转发视频 铺满 borderRadius 6
DynamicArchiveModel? itemContent = switch (item.type) {
DynamicArchiveModel? video = switch (item.type) {
'DYNAMIC_TYPE_AV' => item.modules.moduleDynamic?.major?.archive,
'DYNAMIC_TYPE_UGC_SEASON' => item.modules.moduleDynamic?.major?.ugcSeason,
'DYNAMIC_TYPE_PGC' ||
@@ -53,121 +30,117 @@ Widget videoSeasonWidget(
_ => null,
};
if (itemContent == null) {
if (video == null) {
return const SizedBox.shrink();
}
Widget buildCover() {
if (floor == 1) {
maxWidth -= 24;
}
return Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
width: maxWidth,
height: maxWidth / StyleString.aspectRatio,
src: itemContent.cover,
quality: 40,
),
if (itemContent.badge?.text != null)
PBadge(
text: itemContent.badge!.text,
top: 8.0,
right: 10.0,
bottom: null,
left: null,
type: switch (itemContent.badge!.text) {
'充电专属' => PBadgeType.error,
_ => PBadgeType.primary,
},
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 70,
alignment: Alignment.bottomLeft,
padding: const EdgeInsets.fromLTRB(10, 0, 8, 8),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black54,
],
),
borderRadius: BorderRadius.only(
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
),
),
child: DefaultTextStyle.merge(
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: Colors.white,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (itemContent.durationText != null) ...[
DecoratedBox(
decoration: const BoxDecoration(
color: Colors.black45,
borderRadius: BorderRadius.all(Radius.circular(4)),
),
child: Text(' ${itemContent.durationText} '),
),
const SizedBox(width: 6),
],
if (itemContent.stat != null) ...[
Text('${NumUtil.numFormat(itemContent.stat!.play)}次围观'),
const SizedBox(width: 6),
Text('${NumUtil.numFormat(itemContent.stat!.danmu)}条弹幕'),
],
const Spacer(),
Image.asset(
'assets/images/play.png',
width: 50,
height: 50,
),
],
),
),
),
),
],
);
EdgeInsets padding;
if (floor == 1) {
maxWidth -= 24;
padding = const EdgeInsets.symmetric(horizontal: 12);
} else {
padding = EdgeInsets.zero;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (itemContent.cover != null)
if (floor == 1)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace,
),
child: buildCover(),
)
else
buildCover(),
const SizedBox(height: 6),
if (itemContent.title != null)
Padding(
padding: floor == 1
? const EdgeInsets.only(left: 12, right: 12)
: EdgeInsets.zero,
child: Text(
itemContent.title!,
return Padding(
padding: padding,
child: Column(
spacing: 6,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (video.cover case final cover?)
Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
width: maxWidth,
height: maxWidth / StyleString.aspectRatio,
src: cover,
quality: 40,
),
if (video.badge?.text case final badge?)
PBadge(
text: badge,
top: 8.0,
right: 10.0,
bottom: null,
left: null,
type: switch (badge) {
'充电专属' => PBadgeType.error,
_ => PBadgeType.primary,
},
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
height: 70,
alignment: Alignment.bottomLeft,
padding: const EdgeInsets.fromLTRB(10, 0, 8, 8),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black54,
],
),
borderRadius: BorderRadius.only(
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
),
),
child: DefaultTextStyle.merge(
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: Colors.white,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (video.durationText case final durationText?) ...[
DecoratedBox(
decoration: const BoxDecoration(
color: Colors.black45,
borderRadius: BorderRadius.all(
Radius.circular(4),
),
),
child: Text(' $durationText '),
),
const SizedBox(width: 6),
],
if (video.stat case final stat?) ...[
Text(
'${NumUtil.numFormat(stat.play)}次围观',
),
const SizedBox(width: 6),
Text(
'${NumUtil.numFormat(stat.danmu)}条弹幕',
),
],
const Spacer(),
Image.asset(
'assets/images/play.png',
width: 50,
height: 50,
),
],
),
),
),
),
],
),
if (video.title case final title?)
Text(
title,
maxLines: isDetail ? null : 1,
style: const TextStyle(fontWeight: FontWeight.bold),
overflow: isDetail ? null : TextOverflow.ellipsis,
),
),
],
],
),
);
}

View File

@@ -50,21 +50,33 @@ class _RepostPanelState extends CommonRichTextPubPageState<RepostPanel> {
late final _key = GlobalKey();
late final _pic =
widget.pic ??
widget.item?.modules.moduleDynamic?.major?.archive?.cover ??
widget.item?.modules.moduleDynamic?.major?.pgc?.cover ??
widget.item?.modules.moduleDynamic?.major?.opus?.pics?.firstOrNull?.url;
late final String? _pic;
late final String _text;
late final String? _uname;
late final _text =
widget.title ??
widget.item?.modules.moduleDynamic?.major?.opus?.summary?.text ??
widget.item?.modules.moduleDynamic?.desc?.text ??
widget.item?.modules.moduleDynamic?.major?.archive?.title ??
widget.item?.modules.moduleDynamic?.major?.pgc?.title ??
'';
@override
void initState() {
super.initState();
late final modules = widget.item?.modules;
late final moduleDynamic = modules?.moduleDynamic;
late final major = moduleDynamic?.major;
late final _uname = widget.uname ?? widget.item?.modules.moduleAuthor?.name;
_pic =
widget.pic ??
major?.archive?.cover ??
major?.pgc?.cover ??
major?.opus?.pics?.firstOrNull?.url;
_text =
widget.title ??
major?.opus?.summary?.text ??
major?.archive?.title ??
major?.pgc?.title ??
moduleDynamic?.desc?.text ??
'';
_uname = widget.uname ?? modules?.moduleAuthor?.name;
}
@override
void dispose() {

View File

@@ -105,11 +105,11 @@ class _MemberShopState extends State<MemberShop>
child: Center(
child: FilledButton.tonal(
onPressed: () {
if (_controller.clickUrl case String clickUrl) {
if (_controller.clickUrl case final clickUrl?) {
final url = Uri.parse(
clickUrl,
).queryParameters['url'];
if (url case String url) {
if (url case final url?) {
Get.toNamed(
'/webview',
parameters: {'url': url},

View File

@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/badge_type.dart';
import 'package:PiliPlus/models_new/space/space_shop/item.dart';
import 'package:PiliPlus/models_new/space/space_shop/net_price.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -28,7 +27,7 @@ class MemberShopItem extends StatelessWidget {
),
child: InkWell(
onTap: () {
if (item.cardUrl case String cardUrl) {
if (item.cardUrl case final cardUrl?) {
Get.toNamed('/webview', parameters: {'url': cardUrl});
}
},
@@ -69,7 +68,7 @@ class MemberShopItem extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (item.netPrice case NetPrice netPrice)
if (item.netPrice case final netPrice?)
Text.rich(
style: TextStyle(color: colorScheme.vipColor),
TextSpan(

View File

@@ -417,14 +417,14 @@ class PageUtils {
// pgc
if (archive.type == 2) {
// jumpUrl
if (archive.jumpUrl case String jumpUrl) {
if (archive.jumpUrl case final jumpUrl?) {
if (viewPgcFromUri(jumpUrl)) {
return;
}
}
// redirectUrl from intro
final res = await VideoHttp.videoIntro(bvid: archive.bvid!);
if (res.dataOrNull?.redirectUrl case String redirectUrl) {
if (res.dataOrNull?.redirectUrl case final redirectUrl?) {
if (viewPgcFromUri(redirectUrl)) {
return;
}
@@ -434,7 +434,7 @@ class PageUtils {
archive.jumpUrl.http2https,
false,
)
case String redirectUrl) {
case final redirectUrl?) {
if (viewPgcFromUri(redirectUrl)) {
return;
}
@@ -479,6 +479,18 @@ class PageUtils {
toLiveRoom(liveRcmd.roomId);
break;
case 'DYNAMIC_TYPE_SUBSCRIPTION_NEW':
LivePlayInfo live = item
.modules
.moduleDynamic!
.major!
.subscriptionNew!
.liveRcmd!
.content!
.livePlayInfo!;
toLiveRoom(live.roomId);
break;
/// 合集查看
case 'DYNAMIC_TYPE_UGC_SEASON':
DynamicArchiveModel ugcSeason =
@@ -508,7 +520,7 @@ class PageUtils {
case 'DYNAMIC_TYPE_MEDIALIST':
if (item.modules.moduleDynamic?.major?.medialist
case Medialist medialist) {
case final medialist?) {
final String? url = medialist.jumpUrl;
if (url != null) {
if (url.contains('medialist/detail/ml')) {