mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: item
chore: clean up widgets Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -1,79 +0,0 @@
|
|||||||
import 'package:PiliPlus/common/widgets/no_splash_factory.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/overlay_pop.dart';
|
|
||||||
import 'package:PiliPlus/models/model_video.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class AnimatedDialog extends StatefulWidget {
|
|
||||||
const AnimatedDialog({
|
|
||||||
super.key,
|
|
||||||
required this.videoItem,
|
|
||||||
required this.closeFn,
|
|
||||||
});
|
|
||||||
|
|
||||||
final BaseVideoItemModel videoItem;
|
|
||||||
final Function closeFn;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => AnimatedDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnimatedDialogState extends State<AnimatedDialog>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
late AnimationController controller;
|
|
||||||
late Animation<double> opacityAnimation;
|
|
||||||
late Animation<double> scaleAnimation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
controller = AnimationController(
|
|
||||||
vsync: this, duration: const Duration(milliseconds: 255));
|
|
||||||
opacityAnimation = Tween<double>(begin: 0.0, end: 0.6)
|
|
||||||
.animate(CurvedAnimation(parent: controller, curve: Curves.linear));
|
|
||||||
scaleAnimation = CurvedAnimation(parent: controller, curve: Curves.linear);
|
|
||||||
controller.addListener(listener);
|
|
||||||
controller.forward();
|
|
||||||
}
|
|
||||||
|
|
||||||
void listener() {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
controller.removeListener(listener);
|
|
||||||
controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void closeFn() async {
|
|
||||||
await controller.reverse();
|
|
||||||
widget.closeFn();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Material(
|
|
||||||
color: Colors.black.withOpacity(opacityAnimation.value),
|
|
||||||
child: InkWell(
|
|
||||||
highlightColor: Colors.transparent,
|
|
||||||
splashColor: Colors.transparent,
|
|
||||||
splashFactory: NoSplashFactory(),
|
|
||||||
onTap: closeFn,
|
|
||||||
child: Center(
|
|
||||||
child: FadeTransition(
|
|
||||||
opacity: scaleAnimation,
|
|
||||||
child: ScaleTransition(
|
|
||||||
scale: scaleAnimation,
|
|
||||||
child: OverlayPop(
|
|
||||||
videoItem: widget.videoItem,
|
|
||||||
closeFn: closeFn,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,351 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
const double _kPanelHeaderCollapsedHeight = kMinInteractiveDimension;
|
|
||||||
|
|
||||||
class _SaltedKey<S, V> extends LocalKey {
|
|
||||||
const _SaltedKey(this.salt, this.value);
|
|
||||||
|
|
||||||
final S salt;
|
|
||||||
final V value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if (other.runtimeType != runtimeType) return false;
|
|
||||||
return other is _SaltedKey<S, V> &&
|
|
||||||
other.salt == salt &&
|
|
||||||
other.value == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, salt, value);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
final String saltString = S == String ? "<'$salt'>" : '<$salt>';
|
|
||||||
final String valueString = V == String ? "<'$value'>" : '<$value>';
|
|
||||||
return '[$saltString $valueString]';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppExpansionPanelList extends StatefulWidget {
|
|
||||||
/// Creates an expansion panel list widget. The [expansionCallback] is
|
|
||||||
/// triggered when an expansion panel expand/collapse button is pushed.
|
|
||||||
///
|
|
||||||
/// The [children] and [animationDuration] arguments must not be null.
|
|
||||||
const AppExpansionPanelList({
|
|
||||||
super.key,
|
|
||||||
required this.children,
|
|
||||||
this.expansionCallback,
|
|
||||||
this.animationDuration = kThemeAnimationDuration,
|
|
||||||
this.expandedHeaderPadding = EdgeInsets.zero,
|
|
||||||
this.dividerColor,
|
|
||||||
this.elevation = 2,
|
|
||||||
}) : _allowOnlyOnePanelOpen = false,
|
|
||||||
initialOpenPanelValue = null;
|
|
||||||
|
|
||||||
/// The children of the expansion panel list. They are laid out in a similar
|
|
||||||
/// fashion to [ListBody].
|
|
||||||
final List<AppExpansionPanel> children;
|
|
||||||
|
|
||||||
/// The callback that gets called whenever one of the expand/collapse buttons
|
|
||||||
/// is pressed. The arguments passed to the callback are the index of the
|
|
||||||
/// pressed panel and whether the panel is currently expanded or not.
|
|
||||||
///
|
|
||||||
/// If AppExpansionPanelList.radio is used, the callback may be called a
|
|
||||||
/// second time if a different panel was previously open. The arguments
|
|
||||||
/// passed to the second callback are the index of the panel that will close
|
|
||||||
/// and false, marking that it will be closed.
|
|
||||||
///
|
|
||||||
/// For AppExpansionPanelList, the callback needs to setState when it's notified
|
|
||||||
/// about the closing/opening panel. On the other hand, the callback for
|
|
||||||
/// AppExpansionPanelList.radio is simply meant to inform the parent widget of
|
|
||||||
/// changes, as the radio panels' open/close states are managed internally.
|
|
||||||
///
|
|
||||||
/// This callback is useful in order to keep track of the expanded/collapsed
|
|
||||||
/// panels in a parent widget that may need to react to these changes.
|
|
||||||
final ExpansionPanelCallback? expansionCallback;
|
|
||||||
|
|
||||||
/// The duration of the expansion animation.
|
|
||||||
final Duration animationDuration;
|
|
||||||
|
|
||||||
// Whether multiple panels can be open simultaneously
|
|
||||||
final bool _allowOnlyOnePanelOpen;
|
|
||||||
|
|
||||||
/// The value of the panel that initially begins open. (This value is
|
|
||||||
/// only used when initializing with the [AppExpansionPanelList.radio]
|
|
||||||
/// constructor.)
|
|
||||||
final Object? initialOpenPanelValue;
|
|
||||||
|
|
||||||
/// The padding that surrounds the panel header when expanded.
|
|
||||||
///
|
|
||||||
/// By default, 16px of space is added to the header vertically (above and below)
|
|
||||||
/// during expansion.
|
|
||||||
final EdgeInsets expandedHeaderPadding;
|
|
||||||
|
|
||||||
/// Defines color for the divider when [AppExpansionPanel.isExpanded] is false.
|
|
||||||
///
|
|
||||||
/// If `dividerColor` is null, then [DividerThemeData.color] is used. If that
|
|
||||||
/// is null, then [ThemeData.dividerColor] is used.
|
|
||||||
final Color? dividerColor;
|
|
||||||
|
|
||||||
/// Defines elevation for the [AppExpansionPanel] while it's expanded.
|
|
||||||
///
|
|
||||||
/// By default, the value of elevation is 2.
|
|
||||||
final double elevation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<AppExpansionPanelList> createState() => _AppExpansionPanelListState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AppExpansionPanelListState extends State<AppExpansionPanelList> {
|
|
||||||
ExpansionPanelRadio? _currentOpenPanel;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
if (widget._allowOnlyOnePanelOpen) {
|
|
||||||
assert(_allIdentifiersUnique(),
|
|
||||||
'All ExpansionPanelRadio identifier values must be unique.');
|
|
||||||
if (widget.initialOpenPanelValue != null) {
|
|
||||||
_currentOpenPanel = searchPanelByValue(
|
|
||||||
widget.children.cast<ExpansionPanelRadio>(),
|
|
||||||
widget.initialOpenPanelValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(AppExpansionPanelList oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
|
|
||||||
if (widget._allowOnlyOnePanelOpen) {
|
|
||||||
assert(_allIdentifiersUnique(),
|
|
||||||
'All ExpansionPanelRadio identifier values must be unique.');
|
|
||||||
// If the previous widget was non-radio AppExpansionPanelList, initialize the
|
|
||||||
// open panel to widget.initialOpenPanelValue
|
|
||||||
if (!oldWidget._allowOnlyOnePanelOpen) {
|
|
||||||
_currentOpenPanel = searchPanelByValue(
|
|
||||||
widget.children.cast<ExpansionPanelRadio>(),
|
|
||||||
widget.initialOpenPanelValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_currentOpenPanel = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _allIdentifiersUnique() {
|
|
||||||
final Map<Object, bool> identifierMap = <Object, bool>{};
|
|
||||||
for (final ExpansionPanelRadio child
|
|
||||||
in widget.children.cast<ExpansionPanelRadio>()) {
|
|
||||||
identifierMap[child.value] = true;
|
|
||||||
}
|
|
||||||
return identifierMap.length == widget.children.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isChildExpanded(int index) {
|
|
||||||
if (widget._allowOnlyOnePanelOpen) {
|
|
||||||
final ExpansionPanelRadio radioWidget =
|
|
||||||
widget.children[index] as ExpansionPanelRadio;
|
|
||||||
return _currentOpenPanel?.value == radioWidget.value;
|
|
||||||
}
|
|
||||||
return widget.children[index].isExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handlePressed(bool isExpanded, int index) {
|
|
||||||
widget.expansionCallback?.call(index, isExpanded);
|
|
||||||
|
|
||||||
if (widget._allowOnlyOnePanelOpen) {
|
|
||||||
final ExpansionPanelRadio pressedChild =
|
|
||||||
widget.children[index] as ExpansionPanelRadio;
|
|
||||||
|
|
||||||
// If another ExpansionPanelRadio was already open, apply its
|
|
||||||
// expansionCallback (if any) to false, because it's closing.
|
|
||||||
for (int childIndex = 0;
|
|
||||||
childIndex < widget.children.length;
|
|
||||||
childIndex += 1) {
|
|
||||||
final ExpansionPanelRadio child =
|
|
||||||
widget.children[childIndex] as ExpansionPanelRadio;
|
|
||||||
if (widget.expansionCallback != null &&
|
|
||||||
childIndex != index &&
|
|
||||||
child.value == _currentOpenPanel?.value) {
|
|
||||||
widget.expansionCallback?.call(childIndex, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_currentOpenPanel = isExpanded ? null : pressedChild;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpansionPanelRadio? searchPanelByValue(
|
|
||||||
List<ExpansionPanelRadio> panels, Object? value) {
|
|
||||||
for (final ExpansionPanelRadio panel in panels) {
|
|
||||||
if (panel.value == value) return panel;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
assert(
|
|
||||||
kElevationToShadow.containsKey(widget.elevation),
|
|
||||||
'Invalid value for elevation. See the kElevationToShadow constant for'
|
|
||||||
' possible elevation values.',
|
|
||||||
);
|
|
||||||
|
|
||||||
final List<MergeableMaterialItem> items = <MergeableMaterialItem>[];
|
|
||||||
|
|
||||||
for (int index = 0; index < widget.children.length; index += 1) {
|
|
||||||
//todo: Uncomment to add gap between selected panels
|
|
||||||
/*if (_isChildExpanded(index) && index != 0 && !_isChildExpanded(index - 1))
|
|
||||||
items.add(MaterialGap(key: _SaltedKey<BuildContext, int>(context, index * 2 - 1)));*/
|
|
||||||
|
|
||||||
final AppExpansionPanel child = widget.children[index];
|
|
||||||
final Widget headerWidget = child.headerBuilder(
|
|
||||||
context,
|
|
||||||
_isChildExpanded(index),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget? expandIconContainer = ExpandIcon(
|
|
||||||
isExpanded: _isChildExpanded(index),
|
|
||||||
onPressed: !child.canTapOnHeader
|
|
||||||
? (bool isExpanded) => _handlePressed(isExpanded, index)
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
if (!child.canTapOnHeader) {
|
|
||||||
final MaterialLocalizations localizations =
|
|
||||||
MaterialLocalizations.of(context);
|
|
||||||
expandIconContainer = Semantics(
|
|
||||||
label: _isChildExpanded(index)
|
|
||||||
? localizations.expandedIconTapHint
|
|
||||||
: localizations.collapsedIconTapHint,
|
|
||||||
container: true,
|
|
||||||
child: expandIconContainer,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final iconContainer = child.iconBuilder;
|
|
||||||
if (iconContainer != null) {
|
|
||||||
expandIconContainer = iconContainer(
|
|
||||||
expandIconContainer,
|
|
||||||
_isChildExpanded(index),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget header = Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: widget.animationDuration,
|
|
||||||
curve: Curves.fastOutSlowIn,
|
|
||||||
margin: _isChildExpanded(index)
|
|
||||||
? widget.expandedHeaderPadding
|
|
||||||
: EdgeInsets.zero,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minHeight: _kPanelHeaderCollapsedHeight),
|
|
||||||
child: headerWidget,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (expandIconContainer != null) expandIconContainer,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
if (child.canTapOnHeader) {
|
|
||||||
header = MergeSemantics(
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () => _handlePressed(_isChildExpanded(index), index),
|
|
||||||
child: header,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
items.add(
|
|
||||||
MaterialSlice(
|
|
||||||
key: _SaltedKey<BuildContext, int>(context, index * 2),
|
|
||||||
color: child.backgroundColor,
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
header,
|
|
||||||
AnimatedCrossFade(
|
|
||||||
firstChild: Container(height: 0.0),
|
|
||||||
secondChild: child.body,
|
|
||||||
firstCurve:
|
|
||||||
const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn),
|
|
||||||
secondCurve:
|
|
||||||
const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn),
|
|
||||||
sizeCurve: Curves.fastOutSlowIn,
|
|
||||||
crossFadeState: _isChildExpanded(index)
|
|
||||||
? CrossFadeState.showSecond
|
|
||||||
: CrossFadeState.showFirst,
|
|
||||||
duration: widget.animationDuration,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_isChildExpanded(index) && index != widget.children.length - 1) {
|
|
||||||
items.add(MaterialGap(
|
|
||||||
key: _SaltedKey<BuildContext, int>(context, index * 2 + 1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MergeableMaterial(
|
|
||||||
hasDividers: true,
|
|
||||||
dividerColor: widget.dividerColor,
|
|
||||||
elevation: widget.elevation,
|
|
||||||
children: items,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef ExpansionPanelIconBuilder = Widget? Function(
|
|
||||||
Widget child,
|
|
||||||
bool isExpanded,
|
|
||||||
);
|
|
||||||
|
|
||||||
class AppExpansionPanel {
|
|
||||||
/// Creates an expansion panel to be used as a child for [ExpansionPanelList].
|
|
||||||
/// See [ExpansionPanelList] for an example on how to use this widget.
|
|
||||||
///
|
|
||||||
/// The [headerBuilder], [body], and [isExpanded] arguments must not be null.
|
|
||||||
AppExpansionPanel({
|
|
||||||
required this.headerBuilder,
|
|
||||||
required this.body,
|
|
||||||
this.iconBuilder,
|
|
||||||
this.isExpanded = false,
|
|
||||||
this.canTapOnHeader = false,
|
|
||||||
this.backgroundColor,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The widget builder that builds the expansion panels' header.
|
|
||||||
final ExpansionPanelHeaderBuilder headerBuilder;
|
|
||||||
|
|
||||||
/// The widget builder that builds the expansion panels' icon.
|
|
||||||
///
|
|
||||||
/// If not pass any function, then default icon will be displayed.
|
|
||||||
///
|
|
||||||
/// If builder function return null, then icon will not displayed.
|
|
||||||
final ExpansionPanelIconBuilder? iconBuilder;
|
|
||||||
|
|
||||||
/// The body of the expansion panel that's displayed below the header.
|
|
||||||
///
|
|
||||||
/// This widget is visible only when the panel is expanded.
|
|
||||||
final Widget body;
|
|
||||||
|
|
||||||
/// Whether the panel is expanded.
|
|
||||||
///
|
|
||||||
/// Defaults to false.
|
|
||||||
final bool isExpanded;
|
|
||||||
|
|
||||||
/// Whether tapping on the panel's header will expand/collapse it.
|
|
||||||
///
|
|
||||||
/// Defaults to false.
|
|
||||||
final bool canTapOnHeader;
|
|
||||||
|
|
||||||
/// Defines the background color of the panel.
|
|
||||||
///
|
|
||||||
/// Defaults to [ThemeData.cardColor].
|
|
||||||
final Color? backgroundColor;
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class AppBarWidget extends StatelessWidget implements PreferredSizeWidget {
|
|
||||||
const AppBarWidget({
|
|
||||||
required this.child,
|
|
||||||
required this.controller,
|
|
||||||
required this.visible,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final PreferredSizeWidget child;
|
|
||||||
final AnimationController controller;
|
|
||||||
final bool visible;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Size get preferredSize => child.preferredSize;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
visible ? controller.reverse() : controller.forward();
|
|
||||||
return SlideTransition(
|
|
||||||
position: Tween<Offset>(
|
|
||||||
begin: Offset.zero,
|
|
||||||
end: const Offset(0, -1),
|
|
||||||
).animate(CurvedAnimation(
|
|
||||||
parent: controller,
|
|
||||||
curve: Curves.easeInOutBack,
|
|
||||||
)),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class ContentContainer extends StatelessWidget {
|
|
||||||
final Widget? contentWidget;
|
|
||||||
final Widget? bottomWidget;
|
|
||||||
final bool isScrollable;
|
|
||||||
final Clip? childClipBehavior;
|
|
||||||
|
|
||||||
const ContentContainer({
|
|
||||||
super.key,
|
|
||||||
this.contentWidget,
|
|
||||||
this.bottomWidget,
|
|
||||||
this.isScrollable = true,
|
|
||||||
this.childClipBehavior,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
clipBehavior: childClipBehavior ?? Clip.hardEdge,
|
|
||||||
physics: isScrollable ? null : const NeverScrollableScrollPhysics(),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: constraints.copyWith(
|
|
||||||
minHeight: constraints.maxHeight,
|
|
||||||
maxHeight: double.infinity,
|
|
||||||
),
|
|
||||||
child: IntrinsicHeight(
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
if (contentWidget != null)
|
|
||||||
Expanded(
|
|
||||||
child: contentWidget!,
|
|
||||||
)
|
|
||||||
else
|
|
||||||
const Spacer(),
|
|
||||||
if (bottomWidget != null) bottomWidget!,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -468,10 +468,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel>
|
|||||||
Utils.dateFormat(pubdate),
|
Utils.dateFormat(pubdate),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context)
|
fontSize: 12,
|
||||||
.textTheme
|
|
||||||
.labelSmall!
|
|
||||||
.fontSize,
|
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../../utils/utils.dart';
|
|
||||||
import '../constants.dart';
|
|
||||||
import 'network_img_layer.dart';
|
|
||||||
|
|
||||||
class LiveCard extends StatelessWidget {
|
|
||||||
final dynamic liveItem;
|
|
||||||
|
|
||||||
const LiveCard({
|
|
||||||
super.key,
|
|
||||||
required this.liveItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final String heroTag = Utils.makeHeroTag(liveItem.roomid);
|
|
||||||
|
|
||||||
return Card(
|
|
||||||
elevation: 0,
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(0),
|
|
||||||
side: BorderSide(
|
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {},
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: StyleString.aspectRatio,
|
|
||||||
child: LayoutBuilder(builder:
|
|
||||||
(BuildContext context, BoxConstraints boxConstraints) {
|
|
||||||
final double maxWidth = boxConstraints.maxWidth;
|
|
||||||
final double maxHeight = boxConstraints.maxHeight;
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Hero(
|
|
||||||
tag: heroTag,
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
src: liveItem.cover as String,
|
|
||||||
type: 'emote',
|
|
||||||
width: maxWidth,
|
|
||||||
height: maxHeight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: AnimatedOpacity(
|
|
||||||
opacity: 1,
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
child: liveStat(context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
liveContent(context)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget liveContent(context) {
|
|
||||||
return Padding(
|
|
||||||
// 多列
|
|
||||||
padding: const EdgeInsets.fromLTRB(8, 8, 6, 7),
|
|
||||||
// 单列
|
|
||||||
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
liveItem.title as String,
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
style: const TextStyle(fontSize: 13),
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: Text(
|
|
||||||
liveItem.uname as String,
|
|
||||||
maxLines: 1,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget liveStat(context) {
|
|
||||||
return Container(
|
|
||||||
height: 45,
|
|
||||||
padding: const EdgeInsets.only(top: 22, left: 8, right: 8),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topCenter,
|
|
||||||
end: Alignment.bottomCenter,
|
|
||||||
colors: <Color>[
|
|
||||||
Colors.transparent,
|
|
||||||
Colors.black54,
|
|
||||||
],
|
|
||||||
tileMode: TileMode.mirror,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// StatView(
|
|
||||||
// theme: 'white',
|
|
||||||
// view: view,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 8),
|
|
||||||
// StatDanMu(
|
|
||||||
// theme: 'white',
|
|
||||||
// danmu: danmaku,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
Text(
|
|
||||||
liveItem.online.toString(),
|
|
||||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class NoSplashFactory extends InteractiveInkFeatureFactory {
|
|
||||||
@override
|
|
||||||
InteractiveInkFeature create(
|
|
||||||
{required MaterialInkController controller,
|
|
||||||
required RenderBox referenceBox,
|
|
||||||
required Offset position,
|
|
||||||
required Color color,
|
|
||||||
required TextDirection textDirection,
|
|
||||||
bool containedInkWell = false,
|
|
||||||
RectCallback? rectCallback,
|
|
||||||
BorderRadius? borderRadius,
|
|
||||||
ShapeBorder? customBorder,
|
|
||||||
double? radius,
|
|
||||||
VoidCallback? onRemoved}) {
|
|
||||||
return _NoInteractiveInkFeature(
|
|
||||||
controller: controller,
|
|
||||||
referenceBox: referenceBox,
|
|
||||||
color: color,
|
|
||||||
onRemoved: onRemoved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NoInteractiveInkFeature extends InteractiveInkFeature {
|
|
||||||
@override
|
|
||||||
void paintFeature(Canvas canvas, Matrix4 transform) {}
|
|
||||||
_NoInteractiveInkFeature({
|
|
||||||
required super.controller,
|
|
||||||
required super.referenceBox,
|
|
||||||
required super.color,
|
|
||||||
super.onRemoved,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:PiliPlus/grpc/app/card/v1/card.pb.dart' as card;
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import '../../utils/download.dart';
|
|
||||||
import '../constants.dart';
|
|
||||||
import 'network_img_layer.dart';
|
|
||||||
|
|
||||||
class OverlayPop extends StatelessWidget {
|
|
||||||
const OverlayPop({super.key, this.videoItem, this.closeFn});
|
|
||||||
|
|
||||||
final dynamic videoItem;
|
|
||||||
final Function? closeFn;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final double imgWidth = min(Get.height, Get.width) - 8 * 2;
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
width: imgWidth,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Stack(
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
width: imgWidth,
|
|
||||||
height: imgWidth / StyleString.aspectRatio,
|
|
||||||
src: videoItem is card.Card
|
|
||||||
? (videoItem as card.Card).smallCoverV5.base.cover
|
|
||||||
: videoItem.pic,
|
|
||||||
quality: 100,
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 8,
|
|
||||||
top: 8,
|
|
||||||
child: Container(
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.black.withOpacity(0.3),
|
|
||||||
borderRadius:
|
|
||||||
const BorderRadius.all(Radius.circular(20))),
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '关闭',
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () => closeFn?.call(),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(12, 10, 8, 10),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: SelectableText(
|
|
||||||
videoItem is card.Card
|
|
||||||
? (videoItem as card.Card).smallCoverV5.base.title
|
|
||||||
: videoItem.title,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
IconButton(
|
|
||||||
tooltip: '保存封面图',
|
|
||||||
onPressed: () async {
|
|
||||||
await DownloadUtils.downloadImg(
|
|
||||||
context,
|
|
||||||
[
|
|
||||||
videoItem is card.Card
|
|
||||||
? (videoItem as card.Card).smallCoverV5.base.cover
|
|
||||||
: videoItem.pic ?? videoItem.cover
|
|
||||||
],
|
|
||||||
);
|
|
||||||
closeFn?.call();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.download, size: 20),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class SliverHeaderDelegate extends SliverPersistentHeaderDelegate {
|
|
||||||
SliverHeaderDelegate({required this.height, required this.child});
|
|
||||||
|
|
||||||
final double height;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(
|
|
||||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
double get maxExtent => height;
|
|
||||||
|
|
||||||
@override
|
|
||||||
double get minExtent => height;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
|
|
||||||
true;
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ abstract class _StatItemBase extends StatelessWidget {
|
|||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
final Object value;
|
final Object value;
|
||||||
final String? theme;
|
final String? theme;
|
||||||
final String? size;
|
|
||||||
final Color? textColor;
|
final Color? textColor;
|
||||||
final double iconSize;
|
final double iconSize;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ abstract class _StatItemBase extends StatelessWidget {
|
|||||||
required this.context,
|
required this.context,
|
||||||
required this.value,
|
required this.value,
|
||||||
this.theme,
|
this.theme,
|
||||||
this.size,
|
|
||||||
this.textColor,
|
this.textColor,
|
||||||
this.iconSize = 13,
|
this.iconSize = 13,
|
||||||
});
|
});
|
||||||
@@ -42,7 +40,7 @@ abstract class _StatItemBase extends StatelessWidget {
|
|||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Text(
|
Text(
|
||||||
Utils.numFormat(value),
|
Utils.numFormat(value),
|
||||||
style: TextStyle(fontSize: size == 'medium' ? 12 : 11, color: color),
|
style: TextStyle(fontSize: 12, color: color),
|
||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
semanticsLabel: semanticsLabel,
|
semanticsLabel: semanticsLabel,
|
||||||
)
|
)
|
||||||
@@ -59,7 +57,6 @@ class StatView extends _StatItemBase {
|
|||||||
required super.value,
|
required super.value,
|
||||||
this.goto,
|
this.goto,
|
||||||
super.theme,
|
super.theme,
|
||||||
super.size,
|
|
||||||
super.textColor,
|
super.textColor,
|
||||||
}) : super(iconSize: 13);
|
}) : super(iconSize: 13);
|
||||||
|
|
||||||
@@ -81,7 +78,6 @@ class StatDanMu extends _StatItemBase {
|
|||||||
required super.context,
|
required super.context,
|
||||||
required super.value,
|
required super.value,
|
||||||
super.theme,
|
super.theme,
|
||||||
super.size,
|
|
||||||
super.textColor,
|
super.textColor,
|
||||||
}) : super(iconSize: 14);
|
}) : super(iconSize: 14);
|
||||||
|
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ class VideoCardH extends StatelessWidget {
|
|||||||
final int aid = videoItem.aid!;
|
final int aid = videoItem.aid!;
|
||||||
final String bvid = videoItem.bvid!;
|
final String bvid = videoItem.bvid!;
|
||||||
String type = 'video';
|
String type = 'video';
|
||||||
// try {
|
|
||||||
// type = videoItem.type;
|
|
||||||
// } catch (_) {}
|
|
||||||
if (videoItem is SearchVideoItemModel) {
|
if (videoItem is SearchVideoItemModel) {
|
||||||
var typeOrNull = (videoItem as SearchVideoItemModel).type;
|
var typeOrNull = (videoItem as SearchVideoItemModel).type;
|
||||||
if (typeOrNull?.isNotEmpty == true) {
|
if (typeOrNull?.isNotEmpty == true) {
|
||||||
@@ -59,11 +56,6 @@ class VideoCardH extends StatelessWidget {
|
|||||||
Semantics(
|
Semantics(
|
||||||
label: Utils.videoItemSemantics(videoItem),
|
label: Utils.videoItemSemantics(videoItem),
|
||||||
excludeSemantics: true,
|
excludeSemantics: true,
|
||||||
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
|
||||||
// for (var item in actions)
|
|
||||||
// CustomSemanticsAction(
|
|
||||||
// label: item.title.isEmpty ? 'label' : item.title): item.onTap!,
|
|
||||||
// },
|
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (onLongPress != null) {
|
if (onLongPress != null) {
|
||||||
@@ -181,10 +173,6 @@ class VideoCardH extends StatelessWidget {
|
|||||||
bottom: 6.0,
|
bottom: 6.0,
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
),
|
),
|
||||||
// if (videoItem.rcmdReason != null &&
|
|
||||||
// videoItem.rcmdReason.content != '')
|
|
||||||
// pBadge(videoItem.rcmdReason.content, context,
|
|
||||||
// 6.0, 6.0, null, null),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -261,36 +249,15 @@ class VideoCardH extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// const Spacer(),
|
|
||||||
// if (videoItem.rcmdReason != null &&
|
|
||||||
// videoItem.rcmdReason.content != '')
|
|
||||||
// Container(
|
|
||||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// borderRadius: BorderRadius.circular(4),
|
|
||||||
// border: Border.all(
|
|
||||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
|
||||||
// ),
|
|
||||||
// child: Text(
|
|
||||||
// videoItem.rcmdReason.content,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 9,
|
|
||||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 4),
|
|
||||||
if (showOwner || showPubdate)
|
if (showOwner || showPubdate)
|
||||||
Expanded(
|
Text(
|
||||||
flex: 0,
|
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
|
||||||
child: Text(
|
maxLines: 1,
|
||||||
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
|
style: TextStyle(
|
||||||
maxLines: 1,
|
fontSize: 12,
|
||||||
style: TextStyle(
|
height: 1,
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
height: 1,
|
overflow: TextOverflow.clip,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
overflow: TextOverflow.clip,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 3),
|
const SizedBox(height: 3),
|
||||||
|
|||||||
@@ -29,107 +29,84 @@ class VideoCardHGrpc extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final int aid = videoItem.smallCoverV5.base.args.aid.toInt();
|
final int aid = videoItem.smallCoverV5.base.args.aid.toInt();
|
||||||
// final String bvid = IdUtils.av2bv(aid);
|
|
||||||
String type = 'video';
|
String type = 'video';
|
||||||
// try {
|
|
||||||
// type = videoItem.type;
|
|
||||||
// } catch (_) {}
|
|
||||||
// List<VideoCustomAction> actions =
|
|
||||||
// VideoCustomActions(videoItem, context).actions;
|
|
||||||
final String heroTag = Utils.makeHeroTag(aid);
|
final String heroTag = Utils.makeHeroTag(aid);
|
||||||
return Stack(children: [
|
return Stack(
|
||||||
Semantics(
|
children: [
|
||||||
// label: Utils.videoItemSemantics(videoItem),
|
Semantics(
|
||||||
excludeSemantics: true,
|
excludeSemantics: true,
|
||||||
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
child: InkWell(
|
||||||
// for (var item in actions)
|
borderRadius: BorderRadius.circular(12),
|
||||||
// CustomSemanticsAction(label: item.title): item.onTap!,
|
onLongPress: () => imageSaveDialog(
|
||||||
// },
|
context: context,
|
||||||
child: InkWell(
|
title: videoItem.smallCoverV5.base.title,
|
||||||
borderRadius: BorderRadius.circular(12),
|
cover: videoItem.smallCoverV5.base.cover,
|
||||||
onLongPress: () => imageSaveDialog(
|
),
|
||||||
context: context,
|
onTap: () async {
|
||||||
title: videoItem.smallCoverV5.base.title,
|
if (type == 'ketang') {
|
||||||
cover: videoItem.smallCoverV5.base.cover,
|
SmartDialog.showToast('课堂视频暂不支持播放');
|
||||||
),
|
return;
|
||||||
onTap: () async {
|
}
|
||||||
if (type == 'ketang') {
|
try {
|
||||||
SmartDialog.showToast('课堂视频暂不支持播放');
|
PiliScheme.routePushFromUrl(videoItem.smallCoverV5.base.uri);
|
||||||
return;
|
} catch (err) {
|
||||||
}
|
SmartDialog.showToast(err.toString());
|
||||||
try {
|
}
|
||||||
PiliScheme.routePushFromUrl(videoItem.smallCoverV5.base.uri);
|
|
||||||
} catch (err) {
|
|
||||||
SmartDialog.showToast(err.toString());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: StyleString.aspectRatio,
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (BuildContext context,
|
|
||||||
BoxConstraints boxConstraints) {
|
|
||||||
final double maxWidth = boxConstraints.maxWidth;
|
|
||||||
final double maxHeight = boxConstraints.maxHeight;
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Hero(
|
|
||||||
tag: heroTag,
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
src: videoItem.smallCoverV5.base.cover,
|
|
||||||
width: maxWidth,
|
|
||||||
height: maxHeight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (videoItem
|
|
||||||
.smallCoverV5.coverRightText1.isNotEmpty)
|
|
||||||
PBadge(
|
|
||||||
text: Utils.timeFormat(
|
|
||||||
videoItem.smallCoverV5.coverRightText1),
|
|
||||||
right: 6.0,
|
|
||||||
bottom: 6.0,
|
|
||||||
type: 'gray',
|
|
||||||
),
|
|
||||||
if (type != 'video')
|
|
||||||
PBadge(
|
|
||||||
text: type,
|
|
||||||
left: 6.0,
|
|
||||||
bottom: 6.0,
|
|
||||||
type: 'primary',
|
|
||||||
),
|
|
||||||
// if (videoItem.rcmdReason != null &&
|
|
||||||
// videoItem.rcmdReason.content != '')
|
|
||||||
// pBadge(videoItem.rcmdReason.content, context,
|
|
||||||
// 6.0, 6.0, null, null),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
videoContent(context),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: StyleString.aspectRatio,
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (BuildContext context,
|
||||||
|
BoxConstraints boxConstraints) {
|
||||||
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Hero(
|
||||||
|
tag: heroTag,
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
src: videoItem.smallCoverV5.base.cover,
|
||||||
|
width: maxWidth,
|
||||||
|
height: maxHeight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (videoItem
|
||||||
|
.smallCoverV5.coverRightText1.isNotEmpty)
|
||||||
|
PBadge(
|
||||||
|
text: Utils.timeFormat(
|
||||||
|
videoItem.smallCoverV5.coverRightText1),
|
||||||
|
right: 6.0,
|
||||||
|
bottom: 6.0,
|
||||||
|
type: 'gray',
|
||||||
|
),
|
||||||
|
if (type != 'video')
|
||||||
|
PBadge(
|
||||||
|
text: type,
|
||||||
|
left: 6.0,
|
||||||
|
bottom: 6.0,
|
||||||
|
type: 'primary',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
videoContent(context),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
// if (source == 'normal')
|
);
|
||||||
// Positioned(
|
|
||||||
// bottom: 0,
|
|
||||||
// right: 0,
|
|
||||||
// child: VideoPopupMenu(
|
|
||||||
// size: 29,
|
|
||||||
// iconSize: 17,
|
|
||||||
// actions: actions,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget videoContent(context) {
|
Widget videoContent(context) {
|
||||||
@@ -150,24 +127,6 @@ class VideoCardHGrpc extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// const Spacer(),
|
|
||||||
// if (videoItem.rcmdReason != null &&
|
|
||||||
// videoItem.rcmdReason.content != '')
|
|
||||||
// Container(
|
|
||||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// borderRadius: BorderRadius.circular(4),
|
|
||||||
// border: Border.all(
|
|
||||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
|
||||||
// ),
|
|
||||||
// child: Text(
|
|
||||||
// videoItem.rcmdReason.content,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 9,
|
|
||||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 4),
|
|
||||||
if (showOwner || showPubdate)
|
if (showOwner || showPubdate)
|
||||||
Text(
|
Text(
|
||||||
videoItem.smallCoverV5.rightDesc1,
|
videoItem.smallCoverV5.rightDesc1,
|
||||||
@@ -190,24 +149,6 @@ class VideoCardHGrpc extends StatelessWidget {
|
|||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// if (showView) ...[
|
|
||||||
// StatView(
|
|
||||||
// theme: 'gray',
|
|
||||||
// view: videoItem.stat.view as int,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 8),
|
|
||||||
// ],
|
|
||||||
// if (showDanmaku)
|
|
||||||
// StatDanMu(
|
|
||||||
// theme: 'gray',
|
|
||||||
// danmu: videoItem.stat.danmu as int,
|
|
||||||
// ),
|
|
||||||
// const Spacer(),
|
|
||||||
// if (source == 'normal') const SizedBox(width: 24),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
NetworkImgLayer(
|
NetworkImgLayer(
|
||||||
src: videoItem.cover,
|
src: videoItem.cover,
|
||||||
// videoItem.season?['cover'] ?? videoItem.cover,
|
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
@@ -191,7 +190,6 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
// videoItem.season?['title'] ?? videoItem.title ?? '',
|
|
||||||
videoItem.title,
|
videoItem.title,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -215,7 +213,7 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
|||||||
: videoItem.publishTimeText ?? '',
|
: videoItem.publishTimeText ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
fontSize: 12,
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
@@ -227,15 +225,12 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
|||||||
StatView(
|
StatView(
|
||||||
context: context,
|
context: context,
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
// view: videoItem.season?['view_content'] ??
|
|
||||||
// videoItem.viewContent,
|
|
||||||
value: videoItem.stat.viewStr,
|
value: videoItem.stat.viewStr,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
context: context,
|
context: context,
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
|
|
||||||
value: videoItem.stat.danmuStr,
|
value: videoItem.stat.danmuStr,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -94,10 +94,6 @@ class VideoCardV extends StatelessWidget {
|
|||||||
Semantics(
|
Semantics(
|
||||||
label: Utils.videoItemSemantics(videoItem),
|
label: Utils.videoItemSemantics(videoItem),
|
||||||
excludeSemantics: true,
|
excludeSemantics: true,
|
||||||
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
|
||||||
// for (var item in actions)
|
|
||||||
// CustomSemanticsAction(label: item.title): item.onTap!,
|
|
||||||
// },
|
|
||||||
child: Card(
|
child: Card(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
@@ -109,6 +105,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
cover: videoItem.pic,
|
cover: videoItem.pic,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
@@ -129,8 +126,6 @@ class VideoCardV extends StatelessWidget {
|
|||||||
size: 'small',
|
size: 'small',
|
||||||
type: 'gray',
|
type: 'gray',
|
||||||
text: Utils.timeFormat(videoItem.duration),
|
text: Utils.timeFormat(videoItem.duration),
|
||||||
// semanticsLabel:
|
|
||||||
// '时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}',
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -162,23 +157,17 @@ class VideoCardV extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.fromLTRB(6, 5, 6, 5),
|
padding: const EdgeInsets.fromLTRB(6, 5, 6, 5),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Expanded(
|
||||||
children: [
|
child: Text(
|
||||||
Expanded(
|
"${videoItem.title}\n",
|
||||||
child: Text("${videoItem.title}\n",
|
maxLines: 2,
|
||||||
// semanticsLabel: "${videoItem.title}",
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 2,
|
style: const TextStyle(
|
||||||
overflow: TextOverflow.ellipsis,
|
height: 1.38,
|
||||||
style: const TextStyle(
|
|
||||||
height: 1.38,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
// const SizedBox(height: 2),
|
|
||||||
videoStat(context),
|
videoStat(context),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -224,7 +213,6 @@ class VideoCardV extends StatelessWidget {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
child: Text(
|
child: Text(
|
||||||
videoItem.owner.name.toString(),
|
videoItem.owner.name.toString(),
|
||||||
// semanticsLabel: "Up主:${videoItem.owner.name}",
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -260,46 +248,32 @@ class VideoCardV extends StatelessWidget {
|
|||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
value: videoItem.stat.danmuStr,
|
value: videoItem.stat.danmuStr,
|
||||||
),
|
),
|
||||||
if (videoItem is RecVideoItemModel) ...<Widget>[
|
if (videoItem is RecVideoItemModel) ...[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Expanded(
|
Text.rich(
|
||||||
flex: 0,
|
maxLines: 1,
|
||||||
child: Text.rich(
|
TextSpan(
|
||||||
maxLines: 1,
|
style: TextStyle(
|
||||||
TextSpan(
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
style: TextStyle(
|
color: Theme.of(context).colorScheme.outline.withOpacity(0.8),
|
||||||
fontSize:
|
),
|
||||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
|
||||||
color: Theme.of(context)
|
),
|
||||||
.colorScheme
|
|
||||||
.outline
|
|
||||||
.withOpacity(0.8),
|
|
||||||
),
|
|
||||||
text:
|
|
||||||
Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
|
|
||||||
)),
|
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
],
|
] else if (videoItem is RecVideoItemAppModel &&
|
||||||
if (videoItem is RecVideoItemAppModel &&
|
|
||||||
videoItem.desc != null &&
|
videoItem.desc != null &&
|
||||||
videoItem.desc!.contains(' · ')) ...<Widget>[
|
videoItem.desc!.contains(' · ')) ...[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Expanded(
|
Text.rich(
|
||||||
flex: 0,
|
maxLines: 1,
|
||||||
child: Text.rich(
|
TextSpan(
|
||||||
maxLines: 1,
|
style: TextStyle(
|
||||||
TextSpan(
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
style: TextStyle(
|
color: Theme.of(context).colorScheme.outline.withOpacity(0.8),
|
||||||
fontSize:
|
),
|
||||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
text: Utils.shortenChineseDateString(
|
||||||
color: Theme.of(context)
|
videoItem.desc!.split(' · ').last)),
|
||||||
.colorScheme
|
),
|
||||||
.outline
|
|
||||||
.withOpacity(0.8),
|
|
||||||
),
|
|
||||||
text: Utils.shortenChineseDateString(
|
|
||||||
videoItem.desc!.split(' · ').last)),
|
|
||||||
)),
|
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -40,55 +40,11 @@ class VideoCardVMemberHome extends StatelessWidget {
|
|||||||
Utils.toViewPage(
|
Utils.toViewPage(
|
||||||
'bvid=${bvid ?? IdUtils.av2bv(int.parse(aid!))}&cid=$cid',
|
'bvid=${bvid ?? IdUtils.av2bv(int.parse(aid!))}&cid=$cid',
|
||||||
arguments: {
|
arguments: {
|
||||||
// 'videoItem': videoItem,
|
|
||||||
'pic': videoItem.cover,
|
'pic': videoItem.cover,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
// 动态
|
|
||||||
// case 'picture':
|
|
||||||
// try {
|
|
||||||
// String dynamicType = 'picture';
|
|
||||||
// String uri = videoItem.uri;
|
|
||||||
// String id = '';
|
|
||||||
// if (videoItem.uri.startsWith('bilibili://article/')) {
|
|
||||||
// // https://www.bilibili.com/read/cv27063554
|
|
||||||
// dynamicType = 'read';
|
|
||||||
// RegExp regex = RegExp(r'\d+');
|
|
||||||
// Match match = regex.firstMatch(videoItem.uri)!;
|
|
||||||
// String matchedNumber = match.group(0)!;
|
|
||||||
// videoItem.param = int.parse(matchedNumber);
|
|
||||||
// id = 'cv${videoItem.param}';
|
|
||||||
// }
|
|
||||||
// if (uri.startsWith('http')) {
|
|
||||||
// String path = Uri.parse(uri).path;
|
|
||||||
// if (isStringNumeric(path.split('/')[1])) {
|
|
||||||
// // 请求接口
|
|
||||||
// var res =
|
|
||||||
// await DynamicsHttp.dynamicDetail(id: path.split('/')[1]);
|
|
||||||
// if (res['status']) {
|
|
||||||
// Get.toNamed('/dynamicDetail', arguments: {
|
|
||||||
// 'item': res['data'],
|
|
||||||
// 'floor': 1,
|
|
||||||
// 'action': 'detail'
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// SmartDialog.showToast(res['msg']);
|
|
||||||
// }
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Get.toNamed('/htmlRender', parameters: {
|
|
||||||
// 'url': uri,
|
|
||||||
// 'title': videoItem.title,
|
|
||||||
// 'id': id,
|
|
||||||
// 'dynamicType': dynamicType
|
|
||||||
// });
|
|
||||||
// } catch (err) {
|
|
||||||
// SmartDialog.showToast(err.toString());
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
default:
|
default:
|
||||||
SmartDialog.showToast(goto);
|
SmartDialog.showToast(goto);
|
||||||
Utils.handleWebview(videoItem.uri ?? '');
|
Utils.handleWebview(videoItem.uri ?? '');
|
||||||
@@ -97,70 +53,57 @@ class VideoCardVMemberHome extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// List<VideoCustomAction> actions =
|
return Stack(
|
||||||
// VideoCustomActions(videoItem, context).actions;
|
children: [
|
||||||
return Stack(children: [
|
Semantics(
|
||||||
Semantics(
|
excludeSemantics: true,
|
||||||
// label: Utils.videoItemSemantics(videoItem),
|
child: Card(
|
||||||
excludeSemantics: true,
|
clipBehavior: Clip.hardEdge,
|
||||||
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
margin: EdgeInsets.zero,
|
||||||
// for (var item in actions)
|
child: InkWell(
|
||||||
// CustomSemanticsAction(label: item.title): item.onTap!,
|
onTap: () => onPushDetail(Utils.makeHeroTag(videoItem.bvid)),
|
||||||
// },
|
onLongPress: () => imageSaveDialog(
|
||||||
child: Card(
|
context: context,
|
||||||
clipBehavior: Clip.hardEdge,
|
title: videoItem.title,
|
||||||
margin: EdgeInsets.zero,
|
cover: videoItem.cover,
|
||||||
child: InkWell(
|
),
|
||||||
onTap: () => onPushDetail(Utils.makeHeroTag(videoItem.bvid)),
|
child: Column(
|
||||||
onLongPress: () => imageSaveDialog(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
context: context,
|
children: [
|
||||||
title: videoItem.title,
|
AspectRatio(
|
||||||
cover: videoItem.cover,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
),
|
child: LayoutBuilder(
|
||||||
child: Column(
|
builder: (context, boxConstraints) {
|
||||||
children: [
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
AspectRatio(
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
aspectRatio: StyleString.aspectRatio,
|
return Stack(
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
children: [
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
NetworkImgLayer(
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
src: videoItem.cover,
|
||||||
return Stack(
|
width: maxWidth,
|
||||||
children: [
|
height: maxHeight,
|
||||||
NetworkImgLayer(
|
),
|
||||||
src: videoItem.cover,
|
if ((videoItem.duration ?? -1) > 0)
|
||||||
width: maxWidth,
|
PBadge(
|
||||||
height: maxHeight,
|
bottom: 6,
|
||||||
),
|
right: 7,
|
||||||
if ((videoItem.duration ?? -1) > 0)
|
size: 'small',
|
||||||
PBadge(
|
type: 'gray',
|
||||||
bottom: 6,
|
text: Utils.timeFormat(videoItem.duration),
|
||||||
right: 7,
|
)
|
||||||
size: 'small',
|
],
|
||||||
type: 'gray',
|
);
|
||||||
text: Utils.timeFormat(videoItem.duration),
|
},
|
||||||
// semanticsLabel:
|
),
|
||||||
// '时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}',
|
),
|
||||||
)
|
videoContent(context, videoItem)
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
}),
|
|
||||||
),
|
|
||||||
videoContent(context, videoItem)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
// if (videoItem.goto == 'av')
|
);
|
||||||
// Positioned(
|
|
||||||
// right: -5,
|
|
||||||
// bottom: -2,
|
|
||||||
// child: VideoPopupMenu(
|
|
||||||
// size: 29,
|
|
||||||
// iconSize: 17,
|
|
||||||
// actions: actions,
|
|
||||||
// )),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,136 +111,14 @@ Widget videoContent(BuildContext context, Item videoItem) {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(6, 5, 6, 5),
|
padding: const EdgeInsets.fromLTRB(6, 5, 6, 5),
|
||||||
child: Column(
|
child: Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
'${videoItem.title}\n',
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
maxLines: 2,
|
||||||
children: [
|
overflow: TextOverflow.ellipsis,
|
||||||
Row(
|
style: const TextStyle(
|
||||||
children: [
|
height: 1.38,
|
||||||
Expanded(
|
),
|
||||||
child: Text('${videoItem.title}\n',
|
|
||||||
// semanticsLabel: "${videoItem.title}",
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: const TextStyle(
|
|
||||||
height: 1.38,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// const Spacer(),
|
|
||||||
// const SizedBox(height: 2),
|
|
||||||
// VideoStat(
|
|
||||||
// videoItem: videoItem,
|
|
||||||
// ),
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// if (videoItem.goto == 'bangumi') ...[
|
|
||||||
// PBadge(
|
|
||||||
// text: videoItem.bangumiBadge,
|
|
||||||
// stack: 'normal',
|
|
||||||
// size: 'small',
|
|
||||||
// type: 'line',
|
|
||||||
// fs: 9,
|
|
||||||
// )
|
|
||||||
// ],
|
|
||||||
// if (videoItem.rcmdReason != null) ...[
|
|
||||||
// PBadge(
|
|
||||||
// text: videoItem.rcmdReason,
|
|
||||||
// stack: 'normal',
|
|
||||||
// size: 'small',
|
|
||||||
// type: 'color',
|
|
||||||
// )
|
|
||||||
// ],
|
|
||||||
// if (videoItem.goto == 'picture') ...[
|
|
||||||
// const PBadge(
|
|
||||||
// text: '动态',
|
|
||||||
// stack: 'normal',
|
|
||||||
// size: 'small',
|
|
||||||
// type: 'line',
|
|
||||||
// fs: 9,
|
|
||||||
// )
|
|
||||||
// ],
|
|
||||||
// if (videoItem.isFollowed == 1) ...[
|
|
||||||
// const PBadge(
|
|
||||||
// text: '已关注',
|
|
||||||
// stack: 'normal',
|
|
||||||
// size: 'small',
|
|
||||||
// type: 'color',
|
|
||||||
// )
|
|
||||||
// ],
|
|
||||||
// Expanded(
|
|
||||||
// flex: 1,
|
|
||||||
// child: Text(
|
|
||||||
// videoItem.author ?? '',
|
|
||||||
// // semanticsLabel: "Up主:${videoItem.owner.name}",
|
|
||||||
// maxLines: 1,
|
|
||||||
// overflow: TextOverflow.clip,
|
|
||||||
// style: TextStyle(
|
|
||||||
// height: 1.5,
|
|
||||||
// fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// if (videoItem.goto == 'av') const SizedBox(width: 10)
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget videoStat(BuildContext context, Item videoItem) {
|
|
||||||
// return Row(
|
|
||||||
// children: [
|
|
||||||
// StatView(
|
|
||||||
// theme: 'gray',
|
|
||||||
// view: videoItem.stat.view,
|
|
||||||
// goto: videoItem.goto,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 6),
|
|
||||||
// if (videoItem.goto != 'picture')
|
|
||||||
// StatDanMu(
|
|
||||||
// theme: 'gray',
|
|
||||||
// danmu: videoItem.stat.danmu,
|
|
||||||
// ),
|
|
||||||
// if (videoItem is RecVideoItemModel) ...<Widget>[
|
|
||||||
// const Spacer(),
|
|
||||||
// Expanded(
|
|
||||||
// flex: 0,
|
|
||||||
// child: RichText(
|
|
||||||
// maxLines: 1,
|
|
||||||
// text: TextSpan(
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
|
||||||
// color:
|
|
||||||
// Theme.of(context).colorScheme.outline.withOpacity(0.8),
|
|
||||||
// ),
|
|
||||||
// text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
|
|
||||||
// )),
|
|
||||||
// const SizedBox(width: 2),
|
|
||||||
// ],
|
|
||||||
// if (videoItem is RecVideoItemAppModel &&
|
|
||||||
// videoItem.desc != null &&
|
|
||||||
// videoItem.desc.contains(' · ')) ...<Widget>[
|
|
||||||
// const Spacer(),
|
|
||||||
// Expanded(
|
|
||||||
// flex: 0,
|
|
||||||
// child: RichText(
|
|
||||||
// maxLines: 1,
|
|
||||||
// text: TextSpan(
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
|
||||||
// color:
|
|
||||||
// Theme.of(context).colorScheme.outline.withOpacity(0.8),
|
|
||||||
// ),
|
|
||||||
// text: Utils.shortenChineseDateString(
|
|
||||||
// videoItem.desc.split(' · ').last)),
|
|
||||||
// )),
|
|
||||||
// const SizedBox(width: 2),
|
|
||||||
// ]
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import 'package:PiliPlus/models/space_archive/data.dart' as space_archive;
|
|||||||
import 'package:PiliPlus/models/space_article/data.dart' as space_article;
|
import 'package:PiliPlus/models/space_article/data.dart' as space_article;
|
||||||
import 'package:PiliPlus/models/space/data.dart' as space_;
|
import 'package:PiliPlus/models/space/data.dart' as space_;
|
||||||
import 'package:PiliPlus/models/space_fav/space_fav.dart';
|
import 'package:PiliPlus/models/space_fav/space_fav.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||||
show ContributeType;
|
show ContributeType;
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ class HisListItem with MultiSelectData {
|
|||||||
int? kid;
|
int? kid;
|
||||||
String? tagName;
|
String? tagName;
|
||||||
int? liveStatus;
|
int? liveStatus;
|
||||||
dynamic isFullScreen;
|
|
||||||
|
|
||||||
HisListItem.fromJson(Map<String, dynamic> json) {
|
HisListItem.fromJson(Map<String, dynamic> json) {
|
||||||
title = json['title'];
|
title = json['title'];
|
||||||
|
|||||||
@@ -341,7 +341,6 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
value: Utils.numFormat(!widget.isLoading
|
value: Utils.numFormat(!widget.isLoading
|
||||||
? widget.bangumiDetail!.stat!['views']
|
? widget.bangumiDetail!.stat!['views']
|
||||||
: bangumiItem!.stat!['views']),
|
: bangumiItem!.stat!['views']),
|
||||||
size: 'medium',
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
@@ -351,7 +350,6 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
? widget
|
? widget
|
||||||
.bangumiDetail!.stat!['danmakus']
|
.bangumiDetail!.stat!['danmakus']
|
||||||
: bangumiItem!.stat!['danmakus']),
|
: bangumiItem!.stat!['danmakus']),
|
||||||
size: 'medium',
|
|
||||||
),
|
),
|
||||||
if (isLandscape) ...[
|
if (isLandscape) ...[
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
|
|||||||
@@ -83,14 +83,12 @@ class _IntroDetailState extends CommonCollapseSlidePageState<IntroDetail> {
|
|||||||
context: context,
|
context: context,
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
value: Utils.numFormat(widget.bangumiDetail.stat!['views']),
|
value: Utils.numFormat(widget.bangumiDetail.stat!['views']),
|
||||||
size: 'medium',
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
context: context,
|
context: context,
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
value: Utils.numFormat(widget.bangumiDetail.stat!['danmakus']),
|
value: Utils.numFormat(widget.bangumiDetail.stat!['danmakus']),
|
||||||
size: 'medium',
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -89,13 +89,9 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
Widget bagumiContent(context) {
|
Widget bagumiContent(context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
// 多列
|
|
||||||
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
||||||
// 单列
|
|
||||||
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
bangumiItem.title,
|
bangumiItem.title,
|
||||||
|
|||||||
@@ -55,22 +55,6 @@ class BangumiCardVMemberHome extends StatelessWidget {
|
|||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// if (bangumiItem.badge != null)
|
|
||||||
// PBadge(
|
|
||||||
// text: bangumiItem.badge,
|
|
||||||
// top: 6,
|
|
||||||
// right: 6,
|
|
||||||
// bottom: null,
|
|
||||||
// left: null),
|
|
||||||
// if (bangumiItem.order != null)
|
|
||||||
// PBadge(
|
|
||||||
// text: bangumiItem.order,
|
|
||||||
// top: null,
|
|
||||||
// right: null,
|
|
||||||
// bottom: 6,
|
|
||||||
// left: 6,
|
|
||||||
// type: 'gray',
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -87,13 +71,9 @@ class BangumiCardVMemberHome extends StatelessWidget {
|
|||||||
Widget bangumiContent(Item bangumiItem) {
|
Widget bangumiContent(Item bangumiItem) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
// 多列
|
|
||||||
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
||||||
// 单列
|
|
||||||
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
bangumiItem.title,
|
bangumiItem.title,
|
||||||
@@ -105,24 +85,6 @@ Widget bangumiContent(Item bangumiItem) {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 1),
|
const SizedBox(height: 1),
|
||||||
// if (bangumiItem.indexShow != null)
|
|
||||||
// Text(
|
|
||||||
// bangumiItem.indexShow,
|
|
||||||
// maxLines: 1,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// if (bangumiItem.progress != null)
|
|
||||||
// Text(
|
|
||||||
// bangumiItem.progress,
|
|
||||||
// maxLines: 1,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -75,13 +75,9 @@ class BangumiCardVPgcIndex extends StatelessWidget {
|
|||||||
Widget bagumiContent(context) {
|
Widget bagumiContent(context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
// 多列
|
|
||||||
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
||||||
// 单列
|
|
||||||
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
bangumiItem['title'],
|
bangumiItem['title'],
|
||||||
@@ -102,15 +98,6 @@ class BangumiCardVPgcIndex extends StatelessWidget {
|
|||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// if (bangumiItem.progress != null)
|
|
||||||
// Text(
|
|
||||||
// bangumiItem.progress,
|
|
||||||
// maxLines: 1,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -69,16 +69,8 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SliverToBoxAdapter(
|
const SliverToBoxAdapter(child: SizedBox(height: 10)),
|
||||||
child: SizedBox(height: 10),
|
SliverList(
|
||||||
),
|
|
||||||
SliverGrid(
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 1,
|
|
||||||
mainAxisExtent: 76,
|
|
||||||
crossAxisSpacing: 0,
|
|
||||||
mainAxisSpacing: 0,
|
|
||||||
),
|
|
||||||
delegate: SliverChildListDelegate(
|
delegate: SliverChildListDelegate(
|
||||||
[
|
[
|
||||||
if (widget.dynamicsController.showLiveItems &&
|
if (widget.dynamicsController.showLiveItems &&
|
||||||
@@ -102,9 +94,7 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SliverToBoxAdapter(
|
const SliverToBoxAdapter(child: SizedBox(height: 200)),
|
||||||
child: SizedBox(height: 200),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -112,119 +102,122 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
Widget upItemBuild(data, i) {
|
Widget upItemBuild(data, i) {
|
||||||
bool isCurrent = widget.dynamicsController.currentMid == data.mid ||
|
bool isCurrent = widget.dynamicsController.currentMid == data.mid ||
|
||||||
widget.dynamicsController.currentMid == -1;
|
widget.dynamicsController.currentMid == -1;
|
||||||
return InkWell(
|
return SizedBox(
|
||||||
onTap: () {
|
height: 76,
|
||||||
feedBack();
|
child: InkWell(
|
||||||
if (data.type == 'up') {
|
onTap: () {
|
||||||
widget.dynamicsController.currentMid = data.mid;
|
feedBack();
|
||||||
// dynamicsController.mid.value = data.mid;
|
if (data.type == 'up') {
|
||||||
widget.dynamicsController
|
widget.dynamicsController.currentMid = data.mid;
|
||||||
..upInfo.value = data
|
// dynamicsController.mid.value = data.mid;
|
||||||
..onSelectUp(data.mid);
|
widget.dynamicsController
|
||||||
// int liveLen = liveList.length;
|
..upInfo.value = data
|
||||||
// int upLen = upList.length;
|
..onSelectUp(data.mid);
|
||||||
// double itemWidth = contentWidth + itemPadding.horizontal;
|
// int liveLen = liveList.length;
|
||||||
// double screenWidth = MediaQuery.sizeOf(context).width;
|
// int upLen = upList.length;
|
||||||
// double moveDistance = 0.0;
|
// double itemWidth = contentWidth + itemPadding.horizontal;
|
||||||
// if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
// double screenWidth = MediaQuery.sizeOf(context).width;
|
||||||
// } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
// double moveDistance = 0.0;
|
||||||
// moveDistance =
|
// if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
||||||
// (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
// } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
||||||
// } else {
|
// moveDistance =
|
||||||
// moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
// (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
||||||
// }
|
// } else {
|
||||||
data.hasUpdate = false;
|
// moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
||||||
// scrollController.animateTo(
|
// }
|
||||||
// moveDistance,
|
data.hasUpdate = false;
|
||||||
// duration: const Duration(milliseconds: 500),
|
// scrollController.animateTo(
|
||||||
// curve: Curves.easeInOut,
|
// moveDistance,
|
||||||
// );
|
// duration: const Duration(milliseconds: 500),
|
||||||
setState(() {});
|
// curve: Curves.easeInOut,
|
||||||
} else if (data.type == 'live') {
|
// );
|
||||||
// LiveItemModel liveItem = LiveItemModel.fromJson({
|
setState(() {});
|
||||||
// 'title': data.title,
|
} else if (data.type == 'live') {
|
||||||
// 'uname': data.uname,
|
// LiveItemModel liveItem = LiveItemModel.fromJson({
|
||||||
// 'face': data.face,
|
// 'title': data.title,
|
||||||
// 'roomid': data.roomId,
|
// 'uname': data.uname,
|
||||||
// });
|
// 'face': data.face,
|
||||||
Get.toNamed('/liveRoom?roomid=${data.roomId}');
|
// 'roomid': data.roomId,
|
||||||
}
|
// });
|
||||||
},
|
Get.toNamed('/liveRoom?roomid=${data.roomId}');
|
||||||
onLongPress: () {
|
}
|
||||||
if (data.mid == -1) {
|
},
|
||||||
return;
|
onLongPress: () {
|
||||||
}
|
if (data.mid == -1) {
|
||||||
String heroTag = Utils.makeHeroTag(data.mid);
|
return;
|
||||||
Get.toNamed('/member?mid=${data.mid}',
|
}
|
||||||
arguments: {'face': data.face, 'heroTag': heroTag});
|
String heroTag = Utils.makeHeroTag(data.mid);
|
||||||
},
|
Get.toNamed('/member?mid=${data.mid}',
|
||||||
child: AnimatedOpacity(
|
arguments: {'face': data.face, 'heroTag': heroTag});
|
||||||
opacity: isCurrent ? 1 : 0.6,
|
},
|
||||||
duration: const Duration(milliseconds: 200),
|
child: AnimatedOpacity(
|
||||||
curve: Curves.easeInOut,
|
opacity: isCurrent ? 1 : 0.6,
|
||||||
child: Column(
|
duration: const Duration(milliseconds: 200),
|
||||||
mainAxisSize: MainAxisSize.min,
|
curve: Curves.easeInOut,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
Stack(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
clipBehavior: Clip.none,
|
children: [
|
||||||
children: [
|
Stack(
|
||||||
Padding(
|
clipBehavior: Clip.none,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
children: [
|
||||||
child: data.face != ''
|
Padding(
|
||||||
? NetworkImgLayer(
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
width: 38,
|
child: data.face != ''
|
||||||
height: 38,
|
? NetworkImgLayer(
|
||||||
src: data.face,
|
width: 38,
|
||||||
type: 'avatar',
|
height: 38,
|
||||||
)
|
src: data.face,
|
||||||
: const CircleAvatar(
|
type: 'avatar',
|
||||||
backgroundColor: Color(0xFF5CB67B),
|
)
|
||||||
backgroundImage: AssetImage(
|
: const CircleAvatar(
|
||||||
'assets/images/logo/logo.png',
|
backgroundColor: Color(0xFF5CB67B),
|
||||||
|
backgroundImage: AssetImage(
|
||||||
|
'assets/images/logo/logo.png',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Positioned(
|
||||||
Positioned(
|
top: data.type == 'live' ? -5 : 0,
|
||||||
top: data.type == 'live' ? -5 : 0,
|
right: data.type == 'live' ? -6 : 4,
|
||||||
right: data.type == 'live' ? -6 : 4,
|
child: Badge(
|
||||||
child: Badge(
|
smallSize: 8,
|
||||||
smallSize: 8,
|
label: data.type == 'live' ? const Text(' Live ') : null,
|
||||||
label: data.type == 'live' ? const Text(' Live ') : null,
|
textColor:
|
||||||
textColor:
|
Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
alignment: AlignmentDirectional.topStart,
|
||||||
alignment: AlignmentDirectional.topStart,
|
isLabelVisible: data.type == 'live' ||
|
||||||
isLabelVisible: data.type == 'live' ||
|
(data.type == 'up' && (data.hasUpdate ?? false)),
|
||||||
(data.type == 'up' && (data.hasUpdate ?? false)),
|
backgroundColor: data.type == 'live'
|
||||||
backgroundColor: data.type == 'live'
|
? Theme.of(context)
|
||||||
? Theme.of(context)
|
.colorScheme
|
||||||
.colorScheme
|
.secondaryContainer
|
||||||
.secondaryContainer
|
.withOpacity(0.75)
|
||||||
.withOpacity(0.75)
|
: Theme.of(context).colorScheme.primary,
|
||||||
: Theme.of(context).colorScheme.primary,
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 3),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: Text(
|
||||||
|
data.uname,
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: widget.dynamicsController.currentMid == data.mid
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.outline,
|
||||||
|
height: 1.1,
|
||||||
|
fontSize: 12.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 3),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
||||||
child: Text(
|
|
||||||
data.uname,
|
|
||||||
overflow: TextOverflow.clip,
|
|
||||||
maxLines: 2,
|
|
||||||
softWrap: true,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: widget.dynamicsController.currentMid == data.mid
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Theme.of(context).colorScheme.outline,
|
|
||||||
height: 1.1,
|
|
||||||
fontSize: 12.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class _FansPageState extends State<FansPage> {
|
|||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||||
mainAxisExtent: 56,
|
mainAxisExtent: 66,
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@@ -11,13 +11,16 @@ Widget fanItem({item}) {
|
|||||||
leading: Hero(
|
leading: Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
width: 38,
|
width: 45,
|
||||||
height: 38,
|
height: 45,
|
||||||
type: 'avatar',
|
type: 'avatar',
|
||||||
src: item.face,
|
src: item.face,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Text(item.uname),
|
title: Text(
|
||||||
|
item.uname,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
item.sign,
|
item.sign,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|||||||
@@ -43,11 +43,7 @@ class _FavArticlePageState extends State<FavArticlePage>
|
|||||||
Widget _buildBody(LoadingState<List<dynamic>?> loadingState) {
|
Widget _buildBody(LoadingState<List<dynamic>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -62,11 +58,7 @@ class _FavArticlePageState extends State<FavArticlePage>
|
|||||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.6,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -56,12 +56,21 @@ class FavArticleItem extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
item['content'],
|
child: Text(
|
||||||
maxLines: 2,
|
item['content'],
|
||||||
overflow: TextOverflow.ellipsis,
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.fontSize,
|
||||||
|
height: 1.42,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
@@ -83,12 +92,15 @@ class FavArticleItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 3),
|
||||||
Text(
|
Text(
|
||||||
'${item['author']['name']} · ${item['pub_time']}',
|
'${item['author']['name']} · ${item['pub_time']}',
|
||||||
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
height: 1,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||||
import 'package:PiliPlus/common/widgets/dialog.dart';
|
import 'package:PiliPlus/common/widgets/dialog.dart';
|
||||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||||
@@ -136,11 +135,7 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
|
|||||||
Widget _buildBody(LoadingState<List<FavArticleModel>?> loadingState) {
|
Widget _buildBody(LoadingState<List<FavArticleModel>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -153,11 +148,7 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
|
|||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.6,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -70,14 +70,21 @@ class FavNoteItem extends StatelessWidget {
|
|||||||
item.summary ?? '',
|
item.summary ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
height: 1,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 3),
|
||||||
Text(
|
Text(
|
||||||
item.message ?? '',
|
item.message ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
height: 1,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||||
import 'package:PiliPlus/common/widgets/dialog.dart';
|
import 'package:PiliPlus/common/widgets/dialog.dart';
|
||||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||||
@@ -160,11 +159,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
|||||||
Widget _buildBody(LoadingState<List<BangumiListItemModel>?> loadingState) {
|
Widget _buildBody(LoadingState<List<BangumiListItemModel>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -177,11 +172,7 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
|||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.6,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -48,11 +48,7 @@ class _FavVideoPageState extends State<FavVideoPage>
|
|||||||
Widget _buildBody(LoadingState<List<FavFolderItemData>?> loadingState) {
|
Widget _buildBody(LoadingState<List<FavFolderItemData>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -67,11 +63,7 @@ class _FavVideoPageState extends State<FavVideoPage>
|
|||||||
bottom: 80 + MediaQuery.paddingOf(context).bottom,
|
bottom: 80 + MediaQuery.paddingOf(context).bottom,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount: loadingState.response!.length,
|
childCount: loadingState.response!.length,
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@@ -430,11 +430,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
Widget _buildBody(LoadingState<List<FavDetailItemData>?> loadingState) {
|
Widget _buildBody(LoadingState<List<FavDetailItemData>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -448,11 +444,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
bottom: MediaQuery.of(context).padding.bottom + 85,
|
bottom: MediaQuery.of(context).padding.bottom + 85,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length) {
|
if (index == loadingState.response!.length) {
|
||||||
|
|||||||
@@ -66,15 +66,6 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onViewFav?.call();
|
onViewFav?.call();
|
||||||
// Utils.toViewPage(
|
|
||||||
// 'bvid=$bvid&cid=${videoItem.cid}${epId?.isNotEmpty == true ? '&epId=$epId' : ''}',
|
|
||||||
// arguments: {
|
|
||||||
// 'videoItem': videoItem,
|
|
||||||
// 'heroTag': Utils.makeHeroTag(id),
|
|
||||||
// 'videoType':
|
|
||||||
// epId != null ? SearchType.media_bangumi : SearchType.video,
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
},
|
},
|
||||||
onLongPress: isSort == true
|
onLongPress: isSort == true
|
||||||
? null
|
? null
|
||||||
@@ -180,7 +171,7 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
videoItem.owner.name!,
|
videoItem.owner.name!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
fontSize: 12,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
Loading() => errorWidget(),
|
Loading() => errorWidget(),
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? switch (_favSearchCtr.searchType) {
|
? switch (_favSearchCtr.searchType) {
|
||||||
SearchType.fav || SearchType.history => CustomScrollView(
|
SearchType.fav ||
|
||||||
|
SearchType.history ||
|
||||||
|
SearchType.later =>
|
||||||
|
CustomScrollView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
controller: _favSearchCtr.scrollController,
|
controller: _favSearchCtr.scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
@@ -82,111 +85,71 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: _favSearchCtr.searchType == SearchType.fav
|
||||||
mainAxisSpacing: 2,
|
? Grid.videoCardHDelegate(context, minHeight: 110)
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
: Grid.videoCardHDelegate(context),
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
_favSearchCtr.onLoadMore();
|
_favSearchCtr.onLoadMore();
|
||||||
}
|
}
|
||||||
final item = loadingState.response![index];
|
final item = loadingState.response![index];
|
||||||
return _favSearchCtr.searchType == SearchType.fav
|
if (_favSearchCtr.searchType == SearchType.fav) {
|
||||||
? FavVideoCardH(
|
return FavVideoCardH(
|
||||||
videoItem: item,
|
videoItem: item,
|
||||||
onDelFav: _favSearchCtr.isOwner == true
|
onDelFav: _favSearchCtr.isOwner == true
|
||||||
? () {
|
? () {
|
||||||
_favSearchCtr.onCancelFav(
|
_favSearchCtr.onCancelFav(
|
||||||
index,
|
index,
|
||||||
item.id!,
|
item.id!,
|
||||||
item.type,
|
item.type,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
onViewFav: () {
|
onViewFav: () {
|
||||||
Utils.toViewPage(
|
Utils.toViewPage(
|
||||||
'bvid=${item.bvid}&cid=${item.cid}',
|
'bvid=${item.bvid}&cid=${item.cid}',
|
||||||
arguments: {
|
arguments: {
|
||||||
'videoItem': item,
|
'videoItem': item,
|
||||||
'heroTag':
|
'heroTag': Utils.makeHeroTag(item.bvid),
|
||||||
Utils.makeHeroTag(item.bvid),
|
'sourceType': 'fav',
|
||||||
'sourceType': 'fav',
|
'mediaId': Get.arguments['mediaId'],
|
||||||
'mediaId': Get.arguments['mediaId'],
|
'oid': item.id,
|
||||||
'oid': item.id,
|
'favTitle': Get.arguments['title'],
|
||||||
'favTitle': Get.arguments['title'],
|
'count': Get.arguments['count'],
|
||||||
'count': Get.arguments['count'],
|
'desc': true,
|
||||||
'desc': true,
|
'isContinuePlaying': true,
|
||||||
'isContinuePlaying': true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: HistoryItem(
|
|
||||||
videoItem: item,
|
|
||||||
ctr: _favSearchCtr,
|
|
||||||
onChoose: null,
|
|
||||||
onDelete: (kid, business) {
|
|
||||||
_favSearchCtr.onDelHistory(
|
|
||||||
index, kid, business);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: loadingState.response!.length,
|
);
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SearchType.follow => ListView.builder(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
|
||||||
),
|
|
||||||
controller: _favSearchCtr.scrollController,
|
|
||||||
itemCount: loadingState.response!.length,
|
|
||||||
itemBuilder: ((context, index) {
|
|
||||||
if (index == loadingState.response!.length - 1) {
|
|
||||||
_favSearchCtr.onLoadMore();
|
|
||||||
}
|
|
||||||
return FollowItem(
|
|
||||||
item: loadingState.response![index],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
SearchType.later => CustomScrollView(
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
controller: _favSearchCtr.scrollController,
|
|
||||||
slivers: [
|
|
||||||
SliverPadding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
|
||||||
),
|
|
||||||
sliver: SliverGrid(
|
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(context, index) {
|
|
||||||
if (index == loadingState.response!.length - 1) {
|
|
||||||
_favSearchCtr.onLoadMore();
|
|
||||||
}
|
}
|
||||||
var videoItem = loadingState.response![index];
|
|
||||||
|
if (_favSearchCtr.searchType ==
|
||||||
|
SearchType.history) {
|
||||||
|
return HistoryItem(
|
||||||
|
videoItem: item,
|
||||||
|
ctr: _favSearchCtr,
|
||||||
|
onChoose: null,
|
||||||
|
onDelete: (kid, business) {
|
||||||
|
_favSearchCtr.onDelHistory(
|
||||||
|
index, kid, business);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
VideoCardH(
|
VideoCardH(
|
||||||
videoItem: videoItem,
|
videoItem: item,
|
||||||
source: 'later',
|
source: 'later',
|
||||||
onViewLater: (cid) {
|
onViewLater: (cid) {
|
||||||
Utils.toViewPage(
|
Utils.toViewPage(
|
||||||
'bvid=${videoItem.bvid}&cid=$cid',
|
'bvid=${item.bvid}&cid=$cid',
|
||||||
arguments: {
|
arguments: {
|
||||||
'videoItem': videoItem,
|
'videoItem': item,
|
||||||
'oid': videoItem.aid,
|
'oid': item.aid,
|
||||||
'heroTag':
|
'heroTag': Utils.makeHeroTag(item.bvid),
|
||||||
Utils.makeHeroTag(videoItem.bvid),
|
|
||||||
'sourceType': 'watchLater',
|
'sourceType': 'watchLater',
|
||||||
'count': Get.arguments['count'],
|
'count': Get.arguments['count'],
|
||||||
'favTitle': '稍后再看',
|
'favTitle': '稍后再看',
|
||||||
@@ -205,8 +168,7 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) =>
|
builder: (context, constraints) =>
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
opacity:
|
opacity: item.checked == true ? 1 : 0,
|
||||||
videoItem.checked == true ? 1 : 0,
|
|
||||||
duration:
|
duration:
|
||||||
const Duration(milliseconds: 200),
|
const Duration(milliseconds: 200),
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -224,9 +186,8 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: AnimatedScale(
|
child: AnimatedScale(
|
||||||
scale: videoItem.checked == true
|
scale:
|
||||||
? 1
|
item.checked == true ? 1 : 0,
|
||||||
: 0,
|
|
||||||
duration: const Duration(
|
duration: const Duration(
|
||||||
milliseconds: 250),
|
milliseconds: 250),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
@@ -272,7 +233,7 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
_favSearchCtr.toViewDel(
|
_favSearchCtr.toViewDel(
|
||||||
context,
|
context,
|
||||||
index,
|
index,
|
||||||
videoItem.aid,
|
item.aid,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: Icons.clear,
|
icon: Icons.clear,
|
||||||
@@ -291,6 +252,21 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
SearchType.follow => ListView.builder(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||||
|
),
|
||||||
|
controller: _favSearchCtr.scrollController,
|
||||||
|
itemCount: loadingState.response!.length,
|
||||||
|
itemBuilder: ((context, index) {
|
||||||
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
_favSearchCtr.onLoadMore();
|
||||||
|
}
|
||||||
|
return FollowItem(
|
||||||
|
item: loadingState.response![index],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
: errorWidget(
|
: errorWidget(
|
||||||
callback: _favSearchCtr.onReload,
|
callback: _favSearchCtr.onReload,
|
||||||
|
|||||||
@@ -121,22 +121,3 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// class _FakeAPI {
|
|
||||||
// static const List<String> _kOptions = <String>[
|
|
||||||
// 'aardvark',
|
|
||||||
// 'bobcat',
|
|
||||||
// 'chameleon',
|
|
||||||
// ];
|
|
||||||
// // Searches the options, but injects a fake "network" delay.
|
|
||||||
// static Future<Iterable<String>> search(String query) async {
|
|
||||||
// await Future<void>.delayed(
|
|
||||||
// const Duration(seconds: 1)); // Fake 1 second delay.
|
|
||||||
// if (query == '') {
|
|
||||||
// return const Iterable<String>.empty();
|
|
||||||
// }
|
|
||||||
// return _kOptions.where((String option) {
|
|
||||||
// return option.contains(query.toLowerCase());
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ class FollowItem extends StatelessWidget {
|
|||||||
mid: item.mid,
|
mid: item.mid,
|
||||||
isFollow: item.attribute != -1,
|
isFollow: item.attribute != -1,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
// followStatus: {'special': item.special, 'tag': item.tag},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: FilledButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
|
|||||||
@@ -251,11 +251,7 @@ class _HistoryPageState extends State<HistoryPage>
|
|||||||
Widget _buildBody(LoadingState<List<HisListItem>?> loadingState) {
|
Widget _buildBody(LoadingState<List<HisListItem>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -270,11 +266,7 @@ class _HistoryPageState extends State<HistoryPage>
|
|||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -47,17 +47,6 @@ class HistoryItem extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (videoItem.history.business?.contains('article') == true) {
|
if (videoItem.history.business?.contains('article') == true) {
|
||||||
// int cid = videoItem.history.cid ??
|
|
||||||
// // videoItem.history.oid ??
|
|
||||||
// await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
|
||||||
// Get.toNamed(
|
|
||||||
// '/webview',
|
|
||||||
// parameters: {
|
|
||||||
// 'url': 'https://www.bilibili.com/read/cv$cid',
|
|
||||||
// 'type': 'note',
|
|
||||||
// 'pageTitle': videoItem.title
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
Utils.toDupNamed(
|
Utils.toDupNamed(
|
||||||
'/htmlRender',
|
'/htmlRender',
|
||||||
parameters: {
|
parameters: {
|
||||||
@@ -69,21 +58,12 @@ class HistoryItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
} else if (videoItem.history.business == 'live') {
|
} else if (videoItem.history.business == 'live') {
|
||||||
if (videoItem.liveStatus == 1) {
|
if (videoItem.liveStatus == 1) {
|
||||||
// LiveItemModel liveItem = LiveItemModel.fromJson({
|
|
||||||
// 'face': videoItem.authorFace,
|
|
||||||
// 'roomid': videoItem.history.oid,
|
|
||||||
// 'pic': videoItem.cover,
|
|
||||||
// 'title': videoItem.title,
|
|
||||||
// 'uname': videoItem.authorName,
|
|
||||||
// 'cover': videoItem.cover,
|
|
||||||
// });
|
|
||||||
Get.toNamed('/liveRoom?roomid=${videoItem.history.oid}');
|
Get.toNamed('/liveRoom?roomid=${videoItem.history.oid}');
|
||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast('直播未开播');
|
SmartDialog.showToast('直播未开播');
|
||||||
}
|
}
|
||||||
} else if (videoItem.history.business == 'pgc' ||
|
} else if (videoItem.history.business == 'pgc' ||
|
||||||
videoItem.tagName?.contains('动画') == true) {
|
videoItem.tagName?.contains('动画') == true) {
|
||||||
/// hack
|
|
||||||
var bvid = videoItem.history.bvid;
|
var bvid = videoItem.history.bvid;
|
||||||
if (bvid != null && bvid != '') {
|
if (bvid != null && bvid != '') {
|
||||||
var result = await VideoHttp.videoIntro(bvid: bvid);
|
var result = await VideoHttp.videoIntro(bvid: bvid);
|
||||||
@@ -113,7 +93,6 @@ class HistoryItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int cid = videoItem.history.cid ??
|
int cid = videoItem.history.cid ??
|
||||||
// videoItem.history.oid ??
|
|
||||||
await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||||
Utils.toViewPage(
|
Utils.toViewPage(
|
||||||
'bvid=$bvid&cid=$cid',
|
'bvid=$bvid&cid=$cid',
|
||||||
@@ -138,137 +117,217 @@ class HistoryItem extends StatelessWidget {
|
|||||||
onChoose?.call();
|
onChoose?.call();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Stack(
|
||||||
padding: const EdgeInsets.symmetric(
|
children: [
|
||||||
horizontal: StyleString.safeSpace,
|
Padding(
|
||||||
vertical: 5,
|
padding: const EdgeInsets.symmetric(
|
||||||
),
|
horizontal: StyleString.safeSpace,
|
||||||
child: LayoutBuilder(
|
vertical: 5,
|
||||||
builder: (context, boxConstraints) {
|
),
|
||||||
double width =
|
child: LayoutBuilder(
|
||||||
(boxConstraints.maxWidth - StyleString.cardSpace * 6) / 2;
|
builder: (context, boxConstraints) {
|
||||||
return SizedBox(
|
double width =
|
||||||
height: width / StyleString.aspectRatio,
|
(boxConstraints.maxWidth - StyleString.cardSpace * 6) / 2;
|
||||||
child: Row(
|
return SizedBox(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
height: width / StyleString.aspectRatio,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
Stack(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
clipBehavior: Clip.none,
|
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
Stack(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
clipBehavior: Clip.none,
|
||||||
child: LayoutBuilder(
|
children: [
|
||||||
builder: (context, boxConstraints) {
|
AspectRatio(
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
aspectRatio: StyleString.aspectRatio,
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
child: LayoutBuilder(
|
||||||
return Stack(
|
builder: (context, boxConstraints) {
|
||||||
clipBehavior: Clip.none,
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
children: [
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
NetworkImgLayer(
|
return Stack(
|
||||||
src: (videoItem.cover.isNullOrEmpty
|
clipBehavior: Clip.none,
|
||||||
? videoItem.covers?.first ?? ''
|
children: [
|
||||||
: videoItem.cover),
|
NetworkImgLayer(
|
||||||
width: maxWidth,
|
src: (videoItem.cover.isNullOrEmpty
|
||||||
height: maxHeight,
|
? videoItem.covers?.firstOrNull ?? ''
|
||||||
),
|
: videoItem.cover),
|
||||||
if (!BusinessType
|
width: maxWidth,
|
||||||
.hiddenDurationType.hiddenDurationType
|
height: maxHeight,
|
||||||
.contains(videoItem.history.business))
|
),
|
||||||
PBadge(
|
if (!BusinessType
|
||||||
text: videoItem.progress == -1
|
.hiddenDurationType.hiddenDurationType
|
||||||
? '已看完'
|
.contains(videoItem.history.business))
|
||||||
: '${Utils.timeFormat(videoItem.progress)}/${Utils.timeFormat(videoItem.duration!)}',
|
PBadge(
|
||||||
right: 6.0,
|
text: videoItem.progress == -1
|
||||||
bottom: 8.0,
|
? '已看完'
|
||||||
type: 'gray',
|
: '${Utils.timeFormat(videoItem.progress)}/${Utils.timeFormat(videoItem.duration!)}',
|
||||||
),
|
right: 6.0,
|
||||||
// 右上角
|
bottom: 8.0,
|
||||||
if (BusinessType.showBadge.showBadge
|
type: 'gray',
|
||||||
.contains(videoItem.history.business) ||
|
),
|
||||||
videoItem.history.business ==
|
// 右上角
|
||||||
BusinessType.live.type)
|
if (BusinessType.showBadge.showBadge
|
||||||
PBadge(
|
.contains(
|
||||||
text: videoItem.badge,
|
videoItem.history.business) ||
|
||||||
top: 6.0,
|
videoItem.history.business ==
|
||||||
right: 6.0,
|
BusinessType.live.type)
|
||||||
bottom: null,
|
PBadge(
|
||||||
left: null,
|
text: videoItem.badge,
|
||||||
),
|
top: 6.0,
|
||||||
],
|
right: 6.0,
|
||||||
);
|
bottom: null,
|
||||||
},
|
left: null,
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
Positioned.fill(
|
);
|
||||||
child: AnimatedOpacity(
|
},
|
||||||
opacity: videoItem.checked == true ? 1 : 0,
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
color: Colors.black.withOpacity(0.6),
|
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
),
|
||||||
width: 34,
|
Positioned.fill(
|
||||||
height: 34,
|
child: AnimatedOpacity(
|
||||||
child: AnimatedScale(
|
opacity: videoItem.checked == true ? 1 : 0,
|
||||||
scale: videoItem.checked == true ? 1 : 0,
|
duration: const Duration(milliseconds: 200),
|
||||||
duration: const Duration(milliseconds: 250),
|
child: Container(
|
||||||
curve: Curves.easeInOut,
|
alignment: Alignment.center,
|
||||||
child: IconButton(
|
decoration: BoxDecoration(
|
||||||
tooltip: '取消选择',
|
borderRadius: BorderRadius.circular(10),
|
||||||
style: ButtonStyle(
|
color: Colors.black.withOpacity(0.6),
|
||||||
padding: WidgetStateProperty.all(
|
),
|
||||||
EdgeInsets.zero),
|
child: SizedBox(
|
||||||
backgroundColor:
|
width: 34,
|
||||||
WidgetStateProperty.resolveWith(
|
height: 34,
|
||||||
(states) {
|
child: AnimatedScale(
|
||||||
return Theme.of(context)
|
scale: videoItem.checked == true ? 1 : 0,
|
||||||
.colorScheme
|
duration: const Duration(milliseconds: 250),
|
||||||
.surface
|
curve: Curves.easeInOut,
|
||||||
.withOpacity(0.8);
|
child: IconButton(
|
||||||
|
tooltip: '取消选择',
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: WidgetStateProperty.all(
|
||||||
|
EdgeInsets.zero),
|
||||||
|
backgroundColor:
|
||||||
|
WidgetStateProperty.resolveWith(
|
||||||
|
(states) {
|
||||||
|
return Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surface
|
||||||
|
.withOpacity(0.8);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
feedBack();
|
||||||
|
onChoose?.call();
|
||||||
},
|
},
|
||||||
|
icon: Icon(Icons.done_all_outlined,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
feedBack();
|
|
||||||
onChoose?.call();
|
|
||||||
},
|
|
||||||
icon: Icon(Icons.done_all_outlined,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (videoItem.duration != null &&
|
||||||
|
videoItem.duration != 0 &&
|
||||||
|
videoItem.progress != null &&
|
||||||
|
videoItem.progress != 0)
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: videoProgressIndicator(
|
||||||
|
videoItem.progress == -1
|
||||||
|
? 1
|
||||||
|
: videoItem.progress! / videoItem.duration!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
if (videoItem.duration != null &&
|
const SizedBox(width: 10),
|
||||||
videoItem.duration != 0 &&
|
videoContent(context),
|
||||||
videoItem.progress != null &&
|
|
||||||
videoItem.progress != 0)
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: videoProgressIndicator(
|
|
||||||
videoItem.progress == -1
|
|
||||||
? 1
|
|
||||||
: videoItem.progress! / videoItem.duration!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
);
|
||||||
videoContent(context),
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 12,
|
||||||
|
bottom: 12,
|
||||||
|
child: SizedBox(
|
||||||
|
width: 29,
|
||||||
|
height: 29,
|
||||||
|
child: PopupMenuButton<String>(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
tooltip: '功能菜单',
|
||||||
|
icon: Icon(
|
||||||
|
Icons.more_vert_outlined,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
position: PopupMenuPosition.under,
|
||||||
|
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
|
||||||
|
if (videoItem.authorMid != null &&
|
||||||
|
videoItem.authorName?.isNotEmpty == true)
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
'/member?mid=${videoItem.authorMid}',
|
||||||
|
arguments: {
|
||||||
|
'heroTag': '${videoItem.authorMid}',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
height: 35,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(MdiIcons.accountCircleOutline, size: 16),
|
||||||
|
SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
'访问:${videoItem.authorName}',
|
||||||
|
style: TextStyle(fontSize: 13),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (videoItem.history.business != 'pgc' &&
|
||||||
|
videoItem.badge != '番剧' &&
|
||||||
|
videoItem.tagName?.contains('动画') != true &&
|
||||||
|
videoItem.history.business != 'live' &&
|
||||||
|
videoItem.history.business?.contains('article') != true)
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
onTap: () async {
|
||||||
|
var res = await UserHttp.toViewLater(
|
||||||
|
bvid: videoItem.history.bvid);
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
},
|
||||||
|
height: 35,
|
||||||
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.watch_later_outlined, size: 16),
|
||||||
|
SizedBox(width: 6),
|
||||||
|
Text('稍后再看', style: TextStyle(fontSize: 13))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
onTap: () =>
|
||||||
|
onDelete(videoItem.kid, videoItem.history.business),
|
||||||
|
height: 35,
|
||||||
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.close_outlined, size: 16),
|
||||||
|
SizedBox(width: 6),
|
||||||
|
Text('删除记录', style: TextStyle(fontSize: 13))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -278,123 +337,34 @@ class HistoryItem extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
videoItem.title,
|
child: Text(
|
||||||
textAlign: TextAlign.start,
|
videoItem.title,
|
||||||
style: const TextStyle(
|
|
||||||
letterSpacing: 0.3,
|
|
||||||
),
|
|
||||||
maxLines: videoItem.videos! > 1 ? 1 : 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
if (videoItem.isFullScreen != null) ...[
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
videoItem.isFullScreen,
|
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||||
color: Theme.of(context).colorScheme.outline),
|
height: 1.42,
|
||||||
maxLines: 1,
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
maxLines: videoItem.videos! > 1 ? 1 : 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
const Spacer(),
|
|
||||||
if (videoItem.authorName != '')
|
if (videoItem.authorName != '')
|
||||||
Row(
|
Text(
|
||||||
children: [
|
videoItem.authorName!,
|
||||||
Text(
|
style: TextStyle(
|
||||||
videoItem.authorName!,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
style: TextStyle(
|
color: Theme.of(context).colorScheme.outline,
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
),
|
||||||
color: Theme.of(context).colorScheme.outline,
|
),
|
||||||
),
|
const SizedBox(height: 2),
|
||||||
),
|
Text(
|
||||||
],
|
Utils.dateFormat(videoItem.viewAt!),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
Utils.dateFormat(videoItem.viewAt!),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
// if (videoItem is HisListItem)
|
|
||||||
SizedBox(
|
|
||||||
width: 29,
|
|
||||||
height: 29,
|
|
||||||
child: PopupMenuButton<String>(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
tooltip: '功能菜单',
|
|
||||||
icon: Icon(
|
|
||||||
Icons.more_vert_outlined,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
size: 18,
|
|
||||||
),
|
|
||||||
position: PopupMenuPosition.under,
|
|
||||||
itemBuilder: (BuildContext context) =>
|
|
||||||
<PopupMenuEntry<String>>[
|
|
||||||
if (videoItem.authorMid != null &&
|
|
||||||
videoItem.authorName?.isNotEmpty == true)
|
|
||||||
PopupMenuItem<String>(
|
|
||||||
onTap: () {
|
|
||||||
Get.toNamed(
|
|
||||||
'/member?mid=${videoItem.authorMid}',
|
|
||||||
arguments: {
|
|
||||||
'heroTag': '${videoItem.authorMid}',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
height: 35,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(MdiIcons.accountCircleOutline, size: 16),
|
|
||||||
SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
'访问:${videoItem.authorName}',
|
|
||||||
style: TextStyle(fontSize: 13),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (videoItem.history.business != 'pgc' &&
|
|
||||||
videoItem.badge != '番剧' &&
|
|
||||||
videoItem.tagName?.contains('动画') != true &&
|
|
||||||
videoItem.history.business != 'live' &&
|
|
||||||
videoItem.history.business?.contains('article') != true)
|
|
||||||
PopupMenuItem<String>(
|
|
||||||
onTap: () async {
|
|
||||||
var res = await UserHttp.toViewLater(
|
|
||||||
bvid: videoItem.history.bvid);
|
|
||||||
SmartDialog.showToast(res['msg']);
|
|
||||||
},
|
|
||||||
height: 35,
|
|
||||||
child: const Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.watch_later_outlined, size: 16),
|
|
||||||
SizedBox(width: 6),
|
|
||||||
Text('稍后再看', style: TextStyle(fontSize: 13))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PopupMenuItem<String>(
|
|
||||||
onTap: () =>
|
|
||||||
onDelete(videoItem.kid, videoItem.history.business),
|
|
||||||
height: 35,
|
|
||||||
child: const Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.close_outlined, size: 16),
|
|
||||||
SizedBox(width: 6),
|
|
||||||
Text('删除记录', style: TextStyle(fontSize: 13))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:PiliPlus/pages/mine/view.dart';
|
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
|
||||||
|
|
||||||
class HomeAppBar extends StatelessWidget {
|
|
||||||
const HomeAppBar({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
dynamic userInfo = GStorage.userInfo.get('userInfoCache');
|
|
||||||
return SliverAppBar(
|
|
||||||
// forceElevated: true,
|
|
||||||
toolbarHeight: MediaQuery.of(context).padding.top,
|
|
||||||
expandedHeight: kToolbarHeight + MediaQuery.of(context).padding.top,
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
pinned: true,
|
|
||||||
floating: true,
|
|
||||||
primary: false,
|
|
||||||
flexibleSpace: LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
return FlexibleSpaceBar(
|
|
||||||
background: Column(
|
|
||||||
children: [
|
|
||||||
AppBar(
|
|
||||||
title: const Text(
|
|
||||||
'PiLiPlus',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
letterSpacing: 1,
|
|
||||||
fontFamily: 'ArchivoNarrow',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
Hero(
|
|
||||||
tag: 'searchTag',
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '搜索',
|
|
||||||
onPressed: () {
|
|
||||||
Get.toNamed('/search');
|
|
||||||
},
|
|
||||||
icon: const Icon(CupertinoIcons.search, size: 22),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// IconButton(
|
|
||||||
// onPressed: () {},
|
|
||||||
// icon: const Icon(CupertinoIcons.bell, size: 22),
|
|
||||||
// ),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
|
|
||||||
/// TODO
|
|
||||||
if (userInfo != null) ...[
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => const SizedBox(
|
|
||||||
height: 450,
|
|
||||||
child: MinePage(),
|
|
||||||
),
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
isScrollControlled: true,
|
|
||||||
),
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
type: 'avatar',
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
src: userInfo.face,
|
|
||||||
semanticsLabel: '我的',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
] else ...[
|
|
||||||
IconButton(
|
|
||||||
tooltip: '登录',
|
|
||||||
onPressed: () => showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => const SizedBox(
|
|
||||||
height: 450,
|
|
||||||
child: MinePage(),
|
|
||||||
),
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
isScrollControlled: true,
|
|
||||||
),
|
|
||||||
icon: const Icon(CupertinoIcons.person, size: 22),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
|
|
||||||
const SizedBox(width: 10)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -147,11 +147,7 @@ class _HotPageState extends CommonPageState<HotPage, HotController>
|
|||||||
|
|
||||||
Widget _buildSkeleton() {
|
Widget _buildSkeleton() {
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -166,11 +162,7 @@ class _HotPageState extends CommonPageState<HotPage, HotController>
|
|||||||
Loading() => _buildSkeleton(),
|
Loading() => _buildSkeleton(),
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? SliverGrid(
|
? SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -62,11 +62,7 @@ class _LaterViewChildPageState extends State<LaterViewChildPage>
|
|||||||
Widget _buildBody(LoadingState<List<HotVideoItemModel>?> loadingState) {
|
Widget _buildBody(LoadingState<List<HotVideoItemModel>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -76,11 +72,7 @@ class _LaterViewChildPageState extends State<LaterViewChildPage>
|
|||||||
),
|
),
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? SliverGrid(
|
? SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -142,19 +142,6 @@ class LiveCardV extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// child: RichText(
|
|
||||||
// maxLines: 1,
|
|
||||||
// textAlign: TextAlign.justify,
|
|
||||||
// softWrap: false,
|
|
||||||
// text: TextSpan(
|
|
||||||
// style: const TextStyle(fontSize: 11, color: Colors.white),
|
|
||||||
// children: [
|
|
||||||
// TextSpan(text: liveItem!.areaName!),
|
|
||||||
// TextSpan(text: liveItem!.watchedShow!['text_small']),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,19 +140,6 @@ class LiveCardVFollow extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// child: RichText(
|
|
||||||
// maxLines: 1,
|
|
||||||
// textAlign: TextAlign.justify,
|
|
||||||
// softWrap: false,
|
|
||||||
// text: TextSpan(
|
|
||||||
// style: const TextStyle(fontSize: 11, color: Colors.white),
|
|
||||||
// children: [
|
|
||||||
// TextSpan(text: liveItem!.areaName!),
|
|
||||||
// TextSpan(text: liveItem!.watchedShow!['text_small']),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/space_article/item.dart';
|
import 'package:PiliPlus/models/space_article/item.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/member_article_ctr.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/member_article_ctr.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/widget/item.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/widget/item.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -50,11 +49,7 @@ class _MemberArticleState extends State<MemberArticle>
|
|||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverGrid(
|
SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
@@ -59,27 +59,36 @@ class MemberArticleItem extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (item.title?.isNotEmpty == true) ...[
|
if (item.title?.isNotEmpty == true) ...[
|
||||||
Text(
|
Expanded(
|
||||||
item.title!,
|
child: Text(
|
||||||
maxLines: 2,
|
item.title!,
|
||||||
overflow: TextOverflow.ellipsis,
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.fontSize,
|
||||||
|
height: 1.42,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
],
|
],
|
||||||
Text(
|
Text(
|
||||||
'${item.publishTimeText}',
|
'${item.publishTimeText}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
height: 1,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 3),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
context: context,
|
context: context,
|
||||||
value: item.stats?.view ?? 0,
|
value: item.stats?.view ?? 0,
|
||||||
size: 'medium',
|
|
||||||
goto: 'picture',
|
goto: 'picture',
|
||||||
textColor: Theme.of(context).colorScheme.outline,
|
textColor: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
@@ -87,7 +96,6 @@ class MemberArticleItem extends StatelessWidget {
|
|||||||
StatView(
|
StatView(
|
||||||
context: context,
|
context: context,
|
||||||
goto: 'reply',
|
goto: 'reply',
|
||||||
size: 'medium',
|
|
||||||
value: item.stats?.reply ?? 0,
|
value: item.stats?.reply ?? 0,
|
||||||
textColor: Theme.of(context).colorScheme.outline,
|
textColor: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
@@ -4,7 +4,7 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||||
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_member_home.dart';
|
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_member_home.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/bangumi/member_bangumi_ctr.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/bangumi/member_bangumi_ctr.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -3,9 +3,9 @@ import 'package:PiliPlus/http/member.dart';
|
|||||||
import 'package:PiliPlus/models/space_archive/data.dart';
|
import 'package:PiliPlus/models/space_archive/data.dart';
|
||||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||||
show ContributeType;
|
show ContributeType;
|
||||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
import 'package:PiliPlus/pages/member/controller.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPlus/models/space/data.dart' as space;
|
import 'package:PiliPlus/models/space/data.dart' as space;
|
||||||
|
|
||||||
@@ -3,8 +3,8 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/space_fav/datum.dart';
|
import 'package:PiliPlus/models/space_fav/datum.dart';
|
||||||
import 'package:PiliPlus/models/space_fav/list.dart';
|
import 'package:PiliPlus/models/space_fav/list.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/favorite/member_favorite_ctr.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/favorite/member_favorite_ctr.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/favorite/widget/item.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/favorite/widget/item.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/controller.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/season_series/controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/widget/season_series_card.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/season_series/widget/season_series_card.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/video/member_video.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -51,11 +51,7 @@ class _SeasonSeriesPageState extends State<SeasonSeriesPage>
|
|||||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
@@ -87,7 +87,7 @@ class SeasonSeriesCard extends StatelessWidget {
|
|||||||
Utils.dateFormat(item['meta']['ptime']),
|
Utils.dateFormat(item['meta']['ptime']),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
fontSize: 12,
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
@@ -6,10 +6,10 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
|||||||
import 'package:PiliPlus/common/widgets/video_card_h_member_video.dart';
|
import 'package:PiliPlus/common/widgets/video_card_h_member_video.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/video/member_video_ctr.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||||
show ContributeType;
|
show ContributeType;
|
||||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
import 'package:PiliPlus/pages/member/controller.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -178,11 +178,7 @@ class _MemberVideoState extends State<MemberVideo>
|
|||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (widget.type != ContributeType.season &&
|
if (widget.type != ContributeType.season &&
|
||||||
@@ -5,7 +5,7 @@ import 'package:PiliPlus/models/space_archive/data.dart';
|
|||||||
import 'package:PiliPlus/models/space_archive/episodic_button.dart';
|
import 'package:PiliPlus/models/space_archive/episodic_button.dart';
|
||||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||||
show ContributeType;
|
show ContributeType;
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/id_utils.dart';
|
import 'package:PiliPlus/utils/id_utils.dart';
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/member_article.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/member_article.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/audio/member_audio.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/audio/member_audio.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/season_series_page.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/season_series/season_series_page.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/video/member_video.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute_ctr.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute_ctr.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
@@ -3,12 +3,12 @@ import 'dart:math';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/space/tab2.dart';
|
import 'package:PiliPlus/models/space/tab2.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
import 'package:PiliPlus/pages/member/controller.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../../../../../models/space/item.dart';
|
import '../../../../models/space/item.dart';
|
||||||
|
|
||||||
class MemberContributeCtr extends CommonDataController
|
class MemberContributeCtr extends CommonDataController
|
||||||
with GetTickerProviderStateMixin {
|
with GetTickerProviderStateMixin {
|
||||||
@@ -7,10 +7,10 @@ import 'package:PiliPlus/http/loading_state.dart';
|
|||||||
import 'package:PiliPlus/models/space/data.dart';
|
import 'package:PiliPlus/models/space/data.dart';
|
||||||
import 'package:PiliPlus/models/space/item.dart';
|
import 'package:PiliPlus/models/space/item.dart';
|
||||||
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_member_home.dart';
|
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_member_home.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/widget/item.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/widget/item.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute_ctr.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute_ctr.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_home/widget/fav_item.dart';
|
import 'package:PiliPlus/pages/member/content/member_home/widget/fav_item.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
import 'package:PiliPlus/pages/member/controller.dart';
|
||||||
import 'package:PiliPlus/pages/member_coin/index.dart';
|
import 'package:PiliPlus/pages/member_coin/index.dart';
|
||||||
import 'package:PiliPlus/pages/member_like/index.dart';
|
import 'package:PiliPlus/pages/member_like/index.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
@@ -172,11 +172,7 @@ class _MemberHomeState extends State<MemberHome>
|
|||||||
count: loadingState.response.article.count,
|
count: loadingState.response.article.count,
|
||||||
),
|
),
|
||||||
SliverGrid(
|
SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return MemberArticleItem(
|
return MemberArticleItem(
|
||||||
@@ -1,205 +1,150 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/member.dart';
|
||||||
|
import 'package:PiliPlus/http/video.dart';
|
||||||
|
import 'package:PiliPlus/models/space/data.dart';
|
||||||
|
import 'package:PiliPlus/models/space/item.dart';
|
||||||
|
import 'package:PiliPlus/models/space/tab2.dart';
|
||||||
|
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPlus/http/member.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:PiliPlus/http/user.dart';
|
|
||||||
import 'package:PiliPlus/http/video.dart';
|
|
||||||
import 'package:PiliPlus/models/member/coin.dart';
|
|
||||||
import 'package:PiliPlus/models/member/info.dart';
|
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
|
||||||
|
|
||||||
import '../video/detail/introduction/widgets/group_panel.dart';
|
enum MemberTabType { none, home, dynamic, contribute, favorite, bangumi }
|
||||||
|
|
||||||
class MemberController extends GetxController {
|
extension MemberTabTypeExt on MemberTabType {
|
||||||
int? mid;
|
String get title => ['默认', '首页', '动态', '投稿', '收藏', '番剧'][index];
|
||||||
MemberController({this.mid});
|
}
|
||||||
Rx<MemberInfoModel> memberInfo = MemberInfoModel().obs;
|
|
||||||
late Map userStat;
|
class MemberControllerNew extends CommonDataController<Data, dynamic>
|
||||||
RxString face = ''.obs;
|
with GetTickerProviderStateMixin {
|
||||||
String? heroTag;
|
MemberControllerNew({required this.mid});
|
||||||
late int ownerMid;
|
int mid;
|
||||||
bool specialFollowed = false;
|
RxBool showUname = false.obs;
|
||||||
// 投稿列表
|
String? username;
|
||||||
dynamic userInfo;
|
int? ownerMid;
|
||||||
RxInt attribute = (-1).obs;
|
RxBool isFollow = false.obs;
|
||||||
RxString attributeText = '关注'.obs;
|
RxInt relation = 1.obs;
|
||||||
RxList<MemberCoinsDataModel> recentCoinsList = <MemberCoinsDataModel>[].obs;
|
TabController? tabController;
|
||||||
String? wwebid;
|
late List<Tab> tabs;
|
||||||
|
List<Tab2>? tab2;
|
||||||
|
RxInt contributeInitialIndex = 0.obs;
|
||||||
|
double top = 0;
|
||||||
|
bool? hasSeasonOrSeries;
|
||||||
|
final fromViewAid = Get.parameters['from_view_aid'];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() async {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
mid = mid ?? int.parse(Get.parameters['mid']!);
|
ownerMid = Accounts.main.mid;
|
||||||
userInfo = GStorage.userInfo.get('userInfoCache');
|
queryData();
|
||||||
ownerMid = userInfo != null ? userInfo.mid : -1;
|
|
||||||
try {
|
|
||||||
face.value = Get.arguments['face'] ?? '';
|
|
||||||
heroTag = Get.arguments['heroTag'] ?? '';
|
|
||||||
} catch (_) {}
|
|
||||||
relationSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户信息
|
dynamic live;
|
||||||
Future<Map<String, dynamic>> getInfo() {
|
|
||||||
return Future.wait([getMemberInfo(), getMemberStat(), getMemberView()])
|
|
||||||
.then((res) => res[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getMemberInfo() async {
|
int? silence;
|
||||||
wwebid ??= await Utils.getWwebid(mid);
|
String? endTime;
|
||||||
await getMemberStat();
|
|
||||||
await getMemberView();
|
|
||||||
var res = await MemberHttp.memberInfo(mid: mid, wwebid: wwebid);
|
|
||||||
if (res['status']) {
|
|
||||||
memberInfo.value = res['data'];
|
|
||||||
face.value = res['data'].face;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户状态
|
late final implTabs = const [
|
||||||
Future<Map<String, dynamic>> getMemberStat() async {
|
'home',
|
||||||
var res = await MemberHttp.memberStat(mid: mid);
|
'dynamic',
|
||||||
if (res['status']) {
|
'contribute',
|
||||||
userStat = res['data'];
|
'favorite',
|
||||||
}
|
'bangumi',
|
||||||
return res;
|
];
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户播放数 获赞数
|
@override
|
||||||
Future<Map<String, dynamic>> getMemberView() async {
|
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||||
var res = await MemberHttp.memberView(mid: mid!);
|
Data data = response.response;
|
||||||
if (res['status']) {
|
username = data.card?.name ?? '';
|
||||||
userStat.addAll(res['data']);
|
isFollow.value = data.card?.relation?.isFollow == 1;
|
||||||
|
relation.value = data.relSpecial == 1 ? 2 : data.relation ?? 1;
|
||||||
|
tab2 = data.tab2;
|
||||||
|
live = data.live;
|
||||||
|
silence = data.card?.silence;
|
||||||
|
if ((data.ugcSeason?.count != null && data.ugcSeason?.count != 0) ||
|
||||||
|
data.series?.item?.isNotEmpty == true) {
|
||||||
|
hasSeasonOrSeries = true;
|
||||||
}
|
}
|
||||||
return res;
|
if (data.card?.endTime != null) {
|
||||||
}
|
if (data.card!.endTime == 0) {
|
||||||
|
endTime = ': 永久封禁';
|
||||||
Future delayedUpdateRelation() async {
|
} else if (data.card!.endTime! >
|
||||||
await Future.delayed(const Duration(milliseconds: 1000), () async {
|
DateTime.now().millisecondsSinceEpoch ~/ 1000) {
|
||||||
SmartDialog.showToast('更新状态');
|
endTime =
|
||||||
await relationSearch();
|
':至 ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.fromMillisecondsSinceEpoch(data.card!.endTime! * 1000))}';
|
||||||
memberInfo.update((val) {});
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关注/取关up
|
|
||||||
Future actionRelationMod(BuildContext context) async {
|
|
||||||
if (userInfo == null) {
|
|
||||||
SmartDialog.showToast('账号未登录');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (memberInfo.value.mid == null) {
|
tab2?.retainWhere((item) => implTabs.contains(item.param));
|
||||||
SmartDialog.showToast('尚未获取到用户信息');
|
if (tab2?.isNotEmpty == true) {
|
||||||
return;
|
if (!data.tab!.toJson().values.contains(true) &&
|
||||||
}
|
tab2!.first.param == 'home') {
|
||||||
if (attribute.value == 128) {
|
// remove empty home tab
|
||||||
blockUser(context);
|
tab2!.removeAt(0);
|
||||||
return;
|
}
|
||||||
}
|
if (tab2!.isNotEmpty) {
|
||||||
await showDialog(
|
int initialIndex = -1;
|
||||||
context: context,
|
MemberTabType memberTab = GStorage.memberTab;
|
||||||
builder: (context) {
|
if (memberTab != MemberTabType.none) {
|
||||||
return AlertDialog(
|
initialIndex = tab2!.indexWhere((item) {
|
||||||
title: const Text('操作'),
|
return item.param == memberTab.name;
|
||||||
actions: [
|
});
|
||||||
if (memberInfo.value.isFollowed!) ...[
|
}
|
||||||
TextButton(
|
if (initialIndex == -1) {
|
||||||
onPressed: () async {
|
if (data.defaultTab == 'video') {
|
||||||
final res = await MemberHttp.addUsers(
|
data.defaultTab = 'contribute';
|
||||||
mid, specialFollowed ? '0' : '-10');
|
}
|
||||||
SmartDialog.showToast(res['msg']);
|
initialIndex = tab2!.indexWhere((item) {
|
||||||
if (res['status']) {
|
return item.param == data.defaultTab;
|
||||||
specialFollowed = !specialFollowed;
|
});
|
||||||
}
|
}
|
||||||
Get.back();
|
tabs = tab2!.map((item) => Tab(text: item.title ?? '')).toList();
|
||||||
},
|
tabController = TabController(
|
||||||
child: Text(specialFollowed ? '移除特别关注' : '加入特别关注'),
|
vsync: this,
|
||||||
),
|
length: tabs.length,
|
||||||
TextButton(
|
initialIndex: max(0, initialIndex),
|
||||||
onPressed: () async {
|
|
||||||
await Get.bottomSheet(
|
|
||||||
GroupPanel(mid: mid),
|
|
||||||
isScrollControlled: true,
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
|
||||||
);
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
child: const Text('设置分组'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
var res = await VideoHttp.relationMod(
|
|
||||||
mid: mid!,
|
|
||||||
act: memberInfo.value.isFollowed! ? 2 : 1,
|
|
||||||
reSrc: 11,
|
|
||||||
);
|
|
||||||
SmartDialog.showToast(res['status'] ? "操作成功" : res['msg']);
|
|
||||||
if (res['status']) {
|
|
||||||
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
|
|
||||||
}
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
child: Text(memberInfo.value.isFollowed! ? '取消关注' : '关注'),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Get.back(),
|
|
||||||
child: Text(
|
|
||||||
'取消',
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
await delayedUpdateRelation();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关系查询
|
|
||||||
Future relationSearch() async {
|
|
||||||
if (userInfo == null) return;
|
|
||||||
if (mid == ownerMid) return;
|
|
||||||
var res = await UserHttp.hasFollow(mid!);
|
|
||||||
if (res['status']) {
|
|
||||||
attribute.value = res['data']['attribute'];
|
|
||||||
switch (attribute.value) {
|
|
||||||
case 1:
|
|
||||||
attributeText.value = '悄悄关注';
|
|
||||||
memberInfo.value.isFollowed = true;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
attributeText.value = '已关注';
|
|
||||||
memberInfo.value.isFollowed = true;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
attributeText.value = '已互关';
|
|
||||||
memberInfo.value.isFollowed = true;
|
|
||||||
break;
|
|
||||||
case 128:
|
|
||||||
attributeText.value = '已拉黑';
|
|
||||||
memberInfo.value.isFollowed = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
attributeText.value = '关注';
|
|
||||||
memberInfo.value.isFollowed = false;
|
|
||||||
}
|
}
|
||||||
if (res['data']['special'] == 1) {
|
|
||||||
specialFollowed = true;
|
|
||||||
attributeText.value += ' 🔔';
|
|
||||||
} else {
|
|
||||||
specialFollowed = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast(res['msg']);
|
|
||||||
}
|
}
|
||||||
|
loadingState.value = response;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拉黑用户
|
@override
|
||||||
|
bool handleError(String? errMsg) {
|
||||||
|
tab2 = [
|
||||||
|
Tab2(title: '动态', param: 'dynamic'),
|
||||||
|
Tab2(
|
||||||
|
title: '投稿',
|
||||||
|
param: 'contribute',
|
||||||
|
items: [Item(title: '视频', param: 'video')],
|
||||||
|
),
|
||||||
|
Tab2(title: '收藏', param: 'favorite'),
|
||||||
|
Tab2(title: '追番', param: 'bangumi'),
|
||||||
|
];
|
||||||
|
tabs = tab2!.map((item) => Tab(text: item.title)).toList();
|
||||||
|
tabController = TabController(
|
||||||
|
vsync: this,
|
||||||
|
length: tabs.length,
|
||||||
|
);
|
||||||
|
showUname.value = true;
|
||||||
|
username = errMsg;
|
||||||
|
loadingState.value = LoadingState.success(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LoadingState<Data>> customGetData() => MemberHttp.space(
|
||||||
|
mid: mid,
|
||||||
|
fromViewAid: fromViewAid,
|
||||||
|
);
|
||||||
|
|
||||||
Future blockUser(BuildContext context) async {
|
Future blockUser(BuildContext context) async {
|
||||||
if (userInfo == null) {
|
if (ownerMid == 0) {
|
||||||
SmartDialog.showToast('账号未登录');
|
SmartDialog.showToast('账号未登录');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -208,10 +153,10 @@ class MemberController extends GetxController {
|
|||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('提示'),
|
title: const Text('提示'),
|
||||||
content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'),
|
content: Text(relation.value != -1 ? '确定拉黑UP主?' : '从黑名单移除UP主'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get.back(),
|
onPressed: Get.back,
|
||||||
child: Text(
|
child: Text(
|
||||||
'点错了',
|
'点错了',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@@ -220,18 +165,7 @@ class MemberController extends GetxController {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
Get.back();
|
Get.back();
|
||||||
var res = await VideoHttp.relationMod(
|
_onBlock();
|
||||||
mid: mid!,
|
|
||||||
act: attribute.value != 128 ? 5 : 6,
|
|
||||||
reSrc: 11,
|
|
||||||
);
|
|
||||||
if (res['status']) {
|
|
||||||
attribute.value = attribute.value != 128 ? 128 : 0;
|
|
||||||
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
|
|
||||||
memberInfo.value.isFollowed = false;
|
|
||||||
relationSearch();
|
|
||||||
memberInfo.update((val) {});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: const Text('确认'),
|
child: const Text('确认'),
|
||||||
)
|
)
|
||||||
@@ -242,41 +176,46 @@ class MemberController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void shareUser() {
|
void shareUser() {
|
||||||
Utils.shareText(
|
Utils.shareText('https://space.bilibili.com/$mid');
|
||||||
'${memberInfo.value.name} - https://space.bilibili.com/$mid');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求专栏
|
void _onBlock() async {
|
||||||
Future getMemberSeasons() async {
|
dynamic res = await VideoHttp.relationMod(
|
||||||
if (userInfo == null) return;
|
mid: mid,
|
||||||
var res = await MemberHttp.getMemberSeasons(mid, 1, 10);
|
act: relation.value != -1 ? 5 : 6,
|
||||||
if (!res['status']) {
|
reSrc: 11,
|
||||||
SmartDialog.showToast("用户专栏请求异常:${res['msg']}");
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
relation.value = relation.value != -1 ? -1 : 1;
|
||||||
|
isFollow.value = false;
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求投币视频
|
void onFollow(BuildContext context) async {
|
||||||
Future getRecentCoinVideo() async {
|
if (mid == ownerMid) {
|
||||||
// if (userInfo == null) return;
|
Get.toNamed('/editProfile');
|
||||||
// var res = await MemberHttp.getRecentCoinVideo(mid: mid!);
|
} else if (relation.value == -1) {
|
||||||
// recentCoinsList.value = res['data'];
|
_onBlock();
|
||||||
// return res;
|
} else {
|
||||||
|
if (ownerMid == null) {
|
||||||
|
SmartDialog.showToast('账号未登录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Utils.actionRelationMod(
|
||||||
|
context: context,
|
||||||
|
mid: mid,
|
||||||
|
isFollow: isFollow.value,
|
||||||
|
callback: (attribute) {
|
||||||
|
relation.value = attribute;
|
||||||
|
isFollow.value = attribute != 0;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转查看动态
|
@override
|
||||||
void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid');
|
void onClose() {
|
||||||
|
tabController?.dispose();
|
||||||
// 跳转查看投稿
|
super.onClose();
|
||||||
void pushArchivesPage() async {
|
|
||||||
wwebid ??= await Utils.getWwebid(mid);
|
|
||||||
Get.toNamed('/memberArchive?mid=$mid&wwebid=$wwebid');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 跳转查看专栏
|
|
||||||
void pushSeasonsPage() {}
|
|
||||||
// 跳转查看最近投币
|
|
||||||
void pushRecentCoinsPage() async {
|
|
||||||
if (recentCoinsList.isNotEmpty) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
library member;
|
|
||||||
|
|
||||||
export './controller.dart';
|
|
||||||
export './view.dart';
|
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
import 'package:PiliPlus/common/widgets/dynamic_sliver_appbar.dart';
|
import 'package:PiliPlus/common/widgets/dynamic_sliver_appbar.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/member.dart';
|
||||||
import 'package:PiliPlus/models/space/data.dart';
|
import 'package:PiliPlus/models/space/data.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/bangumi/member_bangumi.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/bangumi/member_bangumi.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/favorite/member_favorite.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/content/favorite/member_favorite.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart';
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_home/member_home.dart';
|
import 'package:PiliPlus/pages/member/content/member_home/member_home.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
import 'package:PiliPlus/pages/member/controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/widget/user_info_card.dart';
|
import 'package:PiliPlus/pages/member/widget/user_info_card.dart';
|
||||||
import 'package:PiliPlus/pages/member/view.dart';
|
|
||||||
import 'package:PiliPlus/pages/member_dynamics/view.dart';
|
import 'package:PiliPlus/pages/member_dynamics/view.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||||
|
|
||||||
@@ -298,3 +300,124 @@ class _MemberPageNewState extends State<MemberPageNew> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReportPanel extends StatefulWidget {
|
||||||
|
const ReportPanel({
|
||||||
|
super.key,
|
||||||
|
required this.name,
|
||||||
|
required this.mid,
|
||||||
|
});
|
||||||
|
|
||||||
|
final dynamic name;
|
||||||
|
final dynamic mid;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ReportPanel> createState() => _ReportPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReportPanelState extends State<ReportPanel> {
|
||||||
|
final List<bool> _reasonList = List.generate(3, (_) => false).toList();
|
||||||
|
final Set<int> _reason = {};
|
||||||
|
int? _reasonV2;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'举报: ${widget.name}',
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text('uid: ${widget.mid}'),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
const Text('举报内容(必选,可多选)'),
|
||||||
|
...List.generate(
|
||||||
|
3,
|
||||||
|
(index) => _checkBoxWidget(
|
||||||
|
_reasonList[index],
|
||||||
|
(value) {
|
||||||
|
setState(() => _reasonList[index] = value);
|
||||||
|
if (value) {
|
||||||
|
_reason.add(index + 1);
|
||||||
|
} else {
|
||||||
|
_reason.remove(index + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
['头像违规', '昵称违规', '签名违规'][index],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text('举报理由(单选,非必选)'),
|
||||||
|
...List.generate(
|
||||||
|
5,
|
||||||
|
(index) => RadioWidget<int>(
|
||||||
|
value: index,
|
||||||
|
groupValue: _reasonV2,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _reasonV2 = value);
|
||||||
|
},
|
||||||
|
title: const ['色情低俗', '不实信息', '违禁', '人身攻击', '赌博诈骗'][index],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: Get.back,
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (_reason.isEmpty) {
|
||||||
|
SmartDialog.showToast('至少选择一项作为举报内容');
|
||||||
|
} else {
|
||||||
|
Get.back();
|
||||||
|
dynamic result = await MemberHttp.reportMember(
|
||||||
|
widget.mid,
|
||||||
|
reason: _reason.join(','),
|
||||||
|
reasonV2: _reasonV2 != null ? _reasonV2! + 1 : null,
|
||||||
|
);
|
||||||
|
if (result['msg'] is String && result['msg'].isNotEmpty) {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('举报失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('确定'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _checkBoxWidget(
|
||||||
|
bool defValue,
|
||||||
|
ValueChanged onChanged,
|
||||||
|
String title,
|
||||||
|
) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => onChanged(!defValue),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: defValue,
|
||||||
|
onChanged: onChanged,
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
Text(title),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
|
||||||
import 'package:PiliPlus/http/member.dart';
|
|
||||||
import 'package:PiliPlus/http/video.dart';
|
|
||||||
import 'package:PiliPlus/models/space/data.dart';
|
|
||||||
import 'package:PiliPlus/models/space/item.dart';
|
|
||||||
import 'package:PiliPlus/models/space/tab2.dart';
|
|
||||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
enum MemberTabType { none, home, dynamic, contribute, favorite, bangumi }
|
|
||||||
|
|
||||||
extension MemberTabTypeExt on MemberTabType {
|
|
||||||
String get title => ['默认', '首页', '动态', '投稿', '收藏', '番剧'][index];
|
|
||||||
}
|
|
||||||
|
|
||||||
class MemberControllerNew extends CommonDataController<Data, dynamic>
|
|
||||||
with GetTickerProviderStateMixin {
|
|
||||||
MemberControllerNew({required this.mid});
|
|
||||||
int mid;
|
|
||||||
RxBool showUname = false.obs;
|
|
||||||
String? username;
|
|
||||||
int? ownerMid;
|
|
||||||
RxBool isFollow = false.obs;
|
|
||||||
RxInt relation = 1.obs;
|
|
||||||
TabController? tabController;
|
|
||||||
late List<Tab> tabs;
|
|
||||||
List<Tab2>? tab2;
|
|
||||||
RxInt contributeInitialIndex = 0.obs;
|
|
||||||
double top = 0;
|
|
||||||
bool? hasSeasonOrSeries;
|
|
||||||
final fromViewAid = Get.parameters['from_view_aid'];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onInit() {
|
|
||||||
super.onInit();
|
|
||||||
ownerMid = Accounts.main.mid;
|
|
||||||
queryData();
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic live;
|
|
||||||
|
|
||||||
int? silence;
|
|
||||||
String? endTime;
|
|
||||||
|
|
||||||
late final implTabs = const [
|
|
||||||
'home',
|
|
||||||
'dynamic',
|
|
||||||
'contribute',
|
|
||||||
'favorite',
|
|
||||||
'bangumi',
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
|
||||||
Data data = response.response;
|
|
||||||
username = data.card?.name ?? '';
|
|
||||||
isFollow.value = data.card?.relation?.isFollow == 1;
|
|
||||||
relation.value = data.relSpecial == 1 ? 2 : data.relation ?? 1;
|
|
||||||
tab2 = data.tab2;
|
|
||||||
live = data.live;
|
|
||||||
silence = data.card?.silence;
|
|
||||||
if ((data.ugcSeason?.count != null && data.ugcSeason?.count != 0) ||
|
|
||||||
data.series?.item?.isNotEmpty == true) {
|
|
||||||
hasSeasonOrSeries = true;
|
|
||||||
}
|
|
||||||
if (data.card?.endTime != null) {
|
|
||||||
if (data.card!.endTime == 0) {
|
|
||||||
endTime = ': 永久封禁';
|
|
||||||
} else if (data.card!.endTime! >
|
|
||||||
DateTime.now().millisecondsSinceEpoch ~/ 1000) {
|
|
||||||
endTime =
|
|
||||||
':至 ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.fromMillisecondsSinceEpoch(data.card!.endTime! * 1000))}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tab2?.retainWhere((item) => implTabs.contains(item.param));
|
|
||||||
if (tab2?.isNotEmpty == true) {
|
|
||||||
if (!data.tab!.toJson().values.contains(true) &&
|
|
||||||
tab2!.first.param == 'home') {
|
|
||||||
// remove empty home tab
|
|
||||||
tab2!.removeAt(0);
|
|
||||||
}
|
|
||||||
if (tab2!.isNotEmpty) {
|
|
||||||
int initialIndex = -1;
|
|
||||||
MemberTabType memberTab = GStorage.memberTab;
|
|
||||||
if (memberTab != MemberTabType.none) {
|
|
||||||
initialIndex = tab2!.indexWhere((item) {
|
|
||||||
return item.param == memberTab.name;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (initialIndex == -1) {
|
|
||||||
if (data.defaultTab == 'video') {
|
|
||||||
data.defaultTab = 'contribute';
|
|
||||||
}
|
|
||||||
initialIndex = tab2!.indexWhere((item) {
|
|
||||||
return item.param == data.defaultTab;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tabs = tab2!.map((item) => Tab(text: item.title ?? '')).toList();
|
|
||||||
tabController = TabController(
|
|
||||||
vsync: this,
|
|
||||||
length: tabs.length,
|
|
||||||
initialIndex: max(0, initialIndex),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadingState.value = response;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool handleError(String? errMsg) {
|
|
||||||
tab2 = [
|
|
||||||
Tab2(title: '动态', param: 'dynamic'),
|
|
||||||
Tab2(
|
|
||||||
title: '投稿',
|
|
||||||
param: 'contribute',
|
|
||||||
items: [Item(title: '视频', param: 'video')],
|
|
||||||
),
|
|
||||||
Tab2(title: '收藏', param: 'favorite'),
|
|
||||||
Tab2(title: '追番', param: 'bangumi'),
|
|
||||||
];
|
|
||||||
tabs = tab2!.map((item) => Tab(text: item.title)).toList();
|
|
||||||
tabController = TabController(
|
|
||||||
vsync: this,
|
|
||||||
length: tabs.length,
|
|
||||||
);
|
|
||||||
showUname.value = true;
|
|
||||||
username = errMsg;
|
|
||||||
loadingState.value = LoadingState.success(null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<LoadingState<Data>> customGetData() => MemberHttp.space(
|
|
||||||
mid: mid,
|
|
||||||
fromViewAid: fromViewAid,
|
|
||||||
);
|
|
||||||
|
|
||||||
Future blockUser(BuildContext context) async {
|
|
||||||
if (ownerMid == 0) {
|
|
||||||
SmartDialog.showToast('账号未登录');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text('提示'),
|
|
||||||
content: Text(relation.value != -1 ? '确定拉黑UP主?' : '从黑名单移除UP主'),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: Get.back,
|
|
||||||
child: Text(
|
|
||||||
'点错了',
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
Get.back();
|
|
||||||
_onBlock();
|
|
||||||
},
|
|
||||||
child: const Text('确认'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void shareUser() {
|
|
||||||
Utils.shareText('https://space.bilibili.com/$mid');
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onBlock() async {
|
|
||||||
dynamic res = await VideoHttp.relationMod(
|
|
||||||
mid: mid,
|
|
||||||
act: relation.value != -1 ? 5 : 6,
|
|
||||||
reSrc: 11,
|
|
||||||
);
|
|
||||||
if (res['status']) {
|
|
||||||
relation.value = relation.value != -1 ? -1 : 1;
|
|
||||||
isFollow.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onFollow(BuildContext context) async {
|
|
||||||
if (mid == ownerMid) {
|
|
||||||
Get.toNamed('/editProfile');
|
|
||||||
} else if (relation.value == -1) {
|
|
||||||
_onBlock();
|
|
||||||
} else {
|
|
||||||
if (ownerMid == null) {
|
|
||||||
SmartDialog.showToast('账号未登录');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Utils.actionRelationMod(
|
|
||||||
context: context,
|
|
||||||
mid: mid,
|
|
||||||
isFollow: isFollow.value,
|
|
||||||
callback: (attribute) {
|
|
||||||
relation.value = attribute;
|
|
||||||
isFollow.value = attribute != 0;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onClose() {
|
|
||||||
tabController?.dispose();
|
|
||||||
super.onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,652 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
|
||||||
import 'package:PiliPlus/http/member.dart';
|
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:PiliPlus/pages/member/index.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
|
|
||||||
import 'widgets/conis.dart';
|
|
||||||
import 'widgets/profile.dart';
|
|
||||||
import 'widgets/seasons.dart';
|
|
||||||
|
|
||||||
@Deprecated('Use MemberPageNew instead')
|
|
||||||
class MemberPage extends StatefulWidget {
|
|
||||||
const MemberPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MemberPage> createState() => _MemberPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MemberPageState extends State<MemberPage> {
|
|
||||||
late String heroTag;
|
|
||||||
late MemberController _memberController;
|
|
||||||
late Future _futureBuilderFuture;
|
|
||||||
late Future _memberSeasonsFuture;
|
|
||||||
late Future _memberCoinsFuture;
|
|
||||||
final ScrollController _extendNestCtr = ScrollController();
|
|
||||||
final StreamController<bool> appbarStream = StreamController<bool>();
|
|
||||||
late int mid;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
mid = int.parse(Get.parameters['mid']!);
|
|
||||||
heroTag = Get.arguments['heroTag'] ?? Utils.makeHeroTag(mid);
|
|
||||||
_memberController = Get.put(MemberController(), tag: heroTag);
|
|
||||||
_futureBuilderFuture = _memberController.getInfo();
|
|
||||||
_memberSeasonsFuture = _memberController.getMemberSeasons();
|
|
||||||
_memberCoinsFuture = _memberController.getRecentCoinVideo();
|
|
||||||
_extendNestCtr.addListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void listener() {
|
|
||||||
final double offset = _extendNestCtr.position.pixels;
|
|
||||||
if (offset > 100) {
|
|
||||||
appbarStream.add(true);
|
|
||||||
} else {
|
|
||||||
appbarStream.add(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_extendNestCtr.removeListener(listener);
|
|
||||||
_extendNestCtr.dispose();
|
|
||||||
appbarStream.close();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
bool isHorizontal = context.width > context.height;
|
|
||||||
return Scaffold(
|
|
||||||
primary: true,
|
|
||||||
body: Column(
|
|
||||||
children: [
|
|
||||||
AppBar(
|
|
||||||
title: StreamBuilder(
|
|
||||||
stream: appbarStream.stream.distinct(),
|
|
||||||
initialData: false,
|
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
|
||||||
return AnimatedOpacity(
|
|
||||||
opacity: snapshot.data ? 1 : 0,
|
|
||||||
curve: Curves.easeOut,
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Obx(
|
|
||||||
() => NetworkImgLayer(
|
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
type: 'avatar',
|
|
||||||
src: _memberController.face.value,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Obx(
|
|
||||||
() => Text(
|
|
||||||
_memberController.memberInfo.value.name ?? '',
|
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.onSurface,
|
|
||||||
fontSize: 14),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
tooltip: '搜索',
|
|
||||||
onPressed: () => Get.toNamed(
|
|
||||||
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
|
|
||||||
icon: const Icon(Icons.search_outlined),
|
|
||||||
),
|
|
||||||
PopupMenuButton(
|
|
||||||
icon: const Icon(Icons.more_vert),
|
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
|
||||||
if (_memberController.ownerMid != _memberController.mid) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
onTap: () => _memberController.blockUser(context),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.block, size: 19),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(_memberController.attribute.value != 128
|
|
||||||
? '加入黑名单'
|
|
||||||
: '移除黑名单'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
PopupMenuItem(
|
|
||||||
onTap: () => _memberController.shareUser(),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.share_outlined, size: 19),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(_memberController.ownerMid != _memberController.mid
|
|
||||||
? '分享UP主'
|
|
||||||
: '分享我的主页'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_memberController.userInfo != null) ...[
|
|
||||||
const PopupMenuDivider(),
|
|
||||||
PopupMenuItem(
|
|
||||||
onTap: () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 20,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
content: ReportPanel(
|
|
||||||
name: _memberController.memberInfo.value.name,
|
|
||||||
mid: mid,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.error_outline,
|
|
||||||
size: 19,
|
|
||||||
color: Theme.of(context).colorScheme.error,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
'举报',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.error),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(
|
|
||||||
parent: BouncingScrollPhysics(),
|
|
||||||
),
|
|
||||||
controller: _extendNestCtr,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 20,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
profileWidget(isHorizontal),
|
|
||||||
Row(children: [
|
|
||||||
const Spacer(),
|
|
||||||
InkWell(
|
|
||||||
onTap: _memberController.pushDynamicsPage,
|
|
||||||
child: const Row(
|
|
||||||
children: [
|
|
||||||
Text('Ta的动态', style: TextStyle(height: 2)),
|
|
||||||
SizedBox(width: 5),
|
|
||||||
Icon(Icons.arrow_forward_ios, size: 19),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
InkWell(
|
|
||||||
onTap: _memberController.pushArchivesPage,
|
|
||||||
child: const Row(
|
|
||||||
children: [
|
|
||||||
Text('Ta的投稿', style: TextStyle(height: 2)),
|
|
||||||
SizedBox(width: 5),
|
|
||||||
Icon(Icons.arrow_forward_ios, size: 19),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {},
|
|
||||||
child: const Row(
|
|
||||||
children: [
|
|
||||||
Text('Ta的专栏', style: TextStyle(height: 2)),
|
|
||||||
SizedBox(width: 5),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
]),
|
|
||||||
MediaQuery.removePadding(
|
|
||||||
removeTop: true,
|
|
||||||
removeBottom: true,
|
|
||||||
context: context,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: StyleString.safeSpace,
|
|
||||||
right: StyleString.safeSpace,
|
|
||||||
),
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: _memberSeasonsFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState ==
|
|
||||||
ConnectionState.done) {
|
|
||||||
if (snapshot.data == null) {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
if (snapshot.data['status']) {
|
|
||||||
Map data = snapshot.data as Map;
|
|
||||||
if (data['data'].seasonsList.isEmpty) {
|
|
||||||
return commonWidget('用户没有设置专栏');
|
|
||||||
} else {
|
|
||||||
return MemberSeasonsPanel(data: data['data']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 请求错误
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
/// 收藏
|
|
||||||
|
|
||||||
/// 追番
|
|
||||||
/// 最近投币
|
|
||||||
Obx(
|
|
||||||
() => _memberController.recentCoinsList.isNotEmpty
|
|
||||||
? ListTile(
|
|
||||||
onTap: () {},
|
|
||||||
title: const Text('最近投币的视频'),
|
|
||||||
// trailing: const Icon(Icons.arrow_forward_outlined,
|
|
||||||
// size: 19),
|
|
||||||
)
|
|
||||||
: const SizedBox(),
|
|
||||||
),
|
|
||||||
MediaQuery.removePadding(
|
|
||||||
removeTop: true,
|
|
||||||
removeBottom: true,
|
|
||||||
context: context,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: StyleString.safeSpace,
|
|
||||||
right: StyleString.safeSpace,
|
|
||||||
),
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: _memberCoinsFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState ==
|
|
||||||
ConnectionState.done) {
|
|
||||||
if (snapshot.data == null) {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
if (snapshot.data['status']) {
|
|
||||||
Map data = snapshot.data as Map;
|
|
||||||
return MemberCoinsPanel(data: data['data']);
|
|
||||||
} else {
|
|
||||||
// 请求错误
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// 最近点赞
|
|
||||||
// ListTile(
|
|
||||||
// onTap: () {},
|
|
||||||
// title: const Text('最近点赞的视频'),
|
|
||||||
// trailing:
|
|
||||||
// const Icon(Icons.arrow_forward_outlined, size: 19),
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget profileWidget(bool isHorizontal) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 18, right: 18, bottom: 20),
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: _futureBuilderFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
|
||||||
snapshot.hasData) {
|
|
||||||
Map data = snapshot.data!;
|
|
||||||
if (data['status']) {
|
|
||||||
return Obx(
|
|
||||||
() => Stack(
|
|
||||||
alignment: AlignmentDirectional.center,
|
|
||||||
children: [profilePanelAndDetailInfo(isHorizontal, false)]),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 骨架屏
|
|
||||||
return profilePanelAndDetailInfo(isHorizontal, true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget profilePanelAndDetailInfo(bool isHorizontal, bool loadingStatus) {
|
|
||||||
if (isHorizontal) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: ProfilePanel(
|
|
||||||
ctr: _memberController, loadingStatus: loadingStatus)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: profileDetailInfo()),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
ProfilePanel(ctr: _memberController, loadingStatus: loadingStatus),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
profileDetailInfo(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget profileDetailInfo() {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
_memberController.memberInfo.value.name ?? '',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: _memberController.memberInfo.value.vip?.status !=
|
|
||||||
null &&
|
|
||||||
_memberController.memberInfo.value.vip!.status! >
|
|
||||||
0 &&
|
|
||||||
_memberController.memberInfo.value.vip!.type == 2
|
|
||||||
? context.vipColor
|
|
||||||
: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
const SizedBox(width: 2),
|
|
||||||
if (_memberController.memberInfo.value.sex == '女')
|
|
||||||
const Icon(
|
|
||||||
FontAwesomeIcons.venus,
|
|
||||||
size: 14,
|
|
||||||
color: Colors.pink,
|
|
||||||
semanticLabel: '女',
|
|
||||||
),
|
|
||||||
if (_memberController.memberInfo.value.sex == '男')
|
|
||||||
const Icon(
|
|
||||||
FontAwesomeIcons.mars,
|
|
||||||
size: 14,
|
|
||||||
color: Colors.blue,
|
|
||||||
semanticLabel: '男',
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
if (_memberController.memberInfo.value.level != null)
|
|
||||||
Image.asset(
|
|
||||||
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
|
|
||||||
height: 11,
|
|
||||||
semanticLabel: '等级${_memberController.memberInfo.value.level}',
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
if (_memberController.memberInfo.value.vip?.status == 1) ...[
|
|
||||||
if (_memberController
|
|
||||||
.memberInfo.value.vip?.label?['img_label_uri_hans'] !=
|
|
||||||
'')
|
|
||||||
CachedNetworkImage(
|
|
||||||
imageUrl: (_memberController.memberInfo.value.vip!
|
|
||||||
.label!['img_label_uri_hans'] as String)
|
|
||||||
.http2https,
|
|
||||||
height: 20,
|
|
||||||
// semanticLabel:
|
|
||||||
// _memberController.memberInfo.value.vip!.label!['text'],
|
|
||||||
)
|
|
||||||
else if (_memberController.memberInfo.value.vip
|
|
||||||
?.label?['img_label_uri_hans_static'] !=
|
|
||||||
'')
|
|
||||||
CachedNetworkImage(
|
|
||||||
imageUrl: (_memberController.memberInfo.value.vip!
|
|
||||||
.label!['img_label_uri_hans_static'] as String)
|
|
||||||
.http2https,
|
|
||||||
height: 20,
|
|
||||||
// semanticLabel:
|
|
||||||
// _memberController.memberInfo.value.vip!.label!['text'],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
Utils.copyText(_memberController.mid.toString());
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'UID: ${_memberController.mid}',
|
|
||||||
style: TextStyle(
|
|
||||||
height: 1,
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
|
||||||
),
|
|
||||||
strutStyle: const StrutStyle(
|
|
||||||
height: 1,
|
|
||||||
leading: 0,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (_memberController.memberInfo.value.official != null &&
|
|
||||||
_memberController.memberInfo.value.official!['title'] != '') ...[
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
Text.rich(
|
|
||||||
maxLines: 2,
|
|
||||||
TextSpan(
|
|
||||||
text: _memberController.memberInfo.value.official!['role'] == 1
|
|
||||||
? '个人认证:'
|
|
||||||
: '机构认证:',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: _memberController.memberInfo.value.official!['title'],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
softWrap: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
SelectableText(
|
|
||||||
_memberController.memberInfo.value.sign ?? '',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget commonWidget(msg) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 20,
|
|
||||||
bottom: 30,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
msg,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReportPanel extends StatefulWidget {
|
|
||||||
const ReportPanel({
|
|
||||||
super.key,
|
|
||||||
required this.name,
|
|
||||||
required this.mid,
|
|
||||||
});
|
|
||||||
|
|
||||||
final dynamic name;
|
|
||||||
final dynamic mid;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ReportPanel> createState() => _ReportPanelState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ReportPanelState extends State<ReportPanel> {
|
|
||||||
final List<bool> _reasonList = List.generate(3, (_) => false).toList();
|
|
||||||
final Set<int> _reason = {};
|
|
||||||
int? _reasonV2;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'举报: ${widget.name}',
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text('uid: ${widget.mid}'),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
const Text('举报内容(必选,可多选)'),
|
|
||||||
...List.generate(
|
|
||||||
3,
|
|
||||||
(index) => _checkBoxWidget(
|
|
||||||
_reasonList[index],
|
|
||||||
(value) {
|
|
||||||
setState(() => _reasonList[index] = value);
|
|
||||||
if (value) {
|
|
||||||
_reason.add(index + 1);
|
|
||||||
} else {
|
|
||||||
_reason.remove(index + 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
['头像违规', '昵称违规', '签名违规'][index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text('举报理由(单选,非必选)'),
|
|
||||||
...List.generate(
|
|
||||||
5,
|
|
||||||
(index) => RadioWidget<int>(
|
|
||||||
value: index,
|
|
||||||
groupValue: _reasonV2,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => _reasonV2 = value);
|
|
||||||
},
|
|
||||||
title: const ['色情低俗', '不实信息', '违禁', '人身攻击', '赌博诈骗'][index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: Get.back,
|
|
||||||
child: Text(
|
|
||||||
'取消',
|
|
||||||
style:
|
|
||||||
TextStyle(color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (_reason.isEmpty) {
|
|
||||||
SmartDialog.showToast('至少选择一项作为举报内容');
|
|
||||||
} else {
|
|
||||||
Get.back();
|
|
||||||
dynamic result = await MemberHttp.reportMember(
|
|
||||||
widget.mid,
|
|
||||||
reason: _reason.join(','),
|
|
||||||
reasonV2: _reasonV2 != null ? _reasonV2! + 1 : null,
|
|
||||||
);
|
|
||||||
if (result['msg'] is String && result['msg'].isNotEmpty) {
|
|
||||||
SmartDialog.showToast(result['msg']);
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast('举报失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const Text('确定'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _checkBoxWidget(
|
|
||||||
bool defValue,
|
|
||||||
ValueChanged onChanged,
|
|
||||||
String title,
|
|
||||||
) {
|
|
||||||
return InkWell(
|
|
||||||
onTap: () => onChanged(!defValue),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Checkbox(
|
|
||||||
value: defValue,
|
|
||||||
onChanged: onChanged,
|
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
Text(title),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/models/member/coin.dart';
|
|
||||||
import 'package:PiliPlus/pages/member_coin/widgets/item.dart';
|
|
||||||
|
|
||||||
class MemberCoinsPanel extends StatelessWidget {
|
|
||||||
final List<MemberCoinsDataModel>? data;
|
|
||||||
const MemberCoinsPanel({super.key, this.data});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, boxConstraints) {
|
|
||||||
return GridView.builder(
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 2, // Use a fixed count for GridView
|
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
|
||||||
mainAxisSpacing: StyleString.safeSpace,
|
|
||||||
childAspectRatio: 0.94,
|
|
||||||
),
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: data!.length,
|
|
||||||
itemBuilder: (context, i) {
|
|
||||||
return MemberCoinsItem(coinItem: data![i]);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
|
||||||
show SourceModel;
|
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:PiliPlus/models/member/info.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
|
|
||||||
class ProfilePanel extends StatelessWidget {
|
|
||||||
final dynamic ctr;
|
|
||||||
final bool loadingStatus;
|
|
||||||
const ProfilePanel({
|
|
||||||
super.key,
|
|
||||||
required this.ctr,
|
|
||||||
this.loadingStatus = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
MemberInfoModel memberInfo = ctr.memberInfo.value;
|
|
||||||
return Builder(
|
|
||||||
builder: ((context) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
Hero(
|
|
||||||
tag: !loadingStatus ? memberInfo.face : ctr.face.value,
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.imageView(
|
|
||||||
imgList: [
|
|
||||||
SourceModel(
|
|
||||||
url:
|
|
||||||
!loadingStatus ? memberInfo.face : ctr.face.value,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
width: 90,
|
|
||||||
height: 90,
|
|
||||||
type: 'avatar',
|
|
||||||
src: !loadingStatus ? memberInfo.face : ctr.face.value,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!loadingStatus &&
|
|
||||||
memberInfo.liveRoom != null &&
|
|
||||||
memberInfo.liveRoom!.liveStatus == 1)
|
|
||||||
Positioned(
|
|
||||||
bottom: 0,
|
|
||||||
left: 14,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
// LiveItemModel liveItem = LiveItemModel.fromJson({
|
|
||||||
// 'title': memberInfo.liveRoom!.title,
|
|
||||||
// 'uname': memberInfo.name,
|
|
||||||
// 'face': memberInfo.face,
|
|
||||||
// 'roomid': memberInfo.liveRoom!.roomId,
|
|
||||||
// 'watched_show': memberInfo.liveRoom!.watchedShow,
|
|
||||||
// });
|
|
||||||
Get.toNamed(
|
|
||||||
'/liveRoom?roomid=${memberInfo.liveRoom!.roomId}',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
borderRadius:
|
|
||||||
const BorderRadius.all(Radius.circular(10)),
|
|
||||||
),
|
|
||||||
child: Row(children: [
|
|
||||||
Image.asset(
|
|
||||||
'assets/images/live.gif',
|
|
||||||
height: 10,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
' 直播中',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelSmall!
|
|
||||||
.fontSize),
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(top: 10, left: 10, right: 10),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Get.toNamed(
|
|
||||||
'/follow?mid=${memberInfo.mid}&name=${memberInfo.name}');
|
|
||||||
},
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
!loadingStatus
|
|
||||||
? ctr.userStat!['following'].toString()
|
|
||||||
: '-',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'关注',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Get.toNamed(
|
|
||||||
'/fan?mid=${memberInfo.mid}&name=${memberInfo.name}');
|
|
||||||
},
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
!loadingStatus
|
|
||||||
? ctr.userStat!['follower'] != null
|
|
||||||
? Utils.numFormat(
|
|
||||||
ctr.userStat!['follower'],
|
|
||||||
)
|
|
||||||
: '-'
|
|
||||||
: '-',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold)),
|
|
||||||
Text(
|
|
||||||
'粉丝',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: null,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
!loadingStatus
|
|
||||||
? ctr.userStat!['likes'] != null
|
|
||||||
? Utils.numFormat(
|
|
||||||
ctr.userStat!['likes'],
|
|
||||||
)
|
|
||||||
: '-'
|
|
||||||
: '-',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold)),
|
|
||||||
Text(
|
|
||||||
'获赞',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) ...[
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Obx(
|
|
||||||
() => Expanded(
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => ctr.actionRelationMod(context),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor: ctr.attribute.value == -1
|
|
||||||
? Colors.transparent
|
|
||||||
: ctr.attribute.value != 0
|
|
||||||
? Theme.of(context).colorScheme.outline
|
|
||||||
: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onPrimary,
|
|
||||||
backgroundColor: ctr.attribute.value != 0
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onInverseSurface
|
|
||||||
: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
child: Obx(() => Text(ctr.attributeText.value)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Get.toNamed(
|
|
||||||
'/whisperDetail',
|
|
||||||
parameters: {
|
|
||||||
'talkerId': ctr.mid.toString(),
|
|
||||||
'name': memberInfo.name!,
|
|
||||||
'face': memberInfo.face!,
|
|
||||||
'mid': ctr.mid.toString(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onInverseSurface,
|
|
||||||
),
|
|
||||||
child: const Text('发消息'),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) ...[
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Get.toNamed('/webview', parameters: {
|
|
||||||
'url': 'https://account.bilibili.com/account/home',
|
|
||||||
'pageTitle': '个人中心(建议浏览器打开)',
|
|
||||||
'type': 'url'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).colorScheme.onPrimary,
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
child: const Text(' 个人中心(web) '),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
if (ctr.ownerMid == -1) ...[
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {},
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor: Theme.of(context).colorScheme.outline,
|
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).colorScheme.onInverseSurface,
|
|
||||||
),
|
|
||||||
child: const Text(' 未登录 '),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
|
||||||
import 'package:PiliPlus/models/member/seasons.dart';
|
|
||||||
import 'package:PiliPlus/pages/member_seasons/widgets/item.dart';
|
|
||||||
|
|
||||||
import '../../../utils/grid.dart';
|
|
||||||
|
|
||||||
class MemberSeasonsPanel extends StatelessWidget {
|
|
||||||
final MemberSeasonsDataModel? data;
|
|
||||||
const MemberSeasonsPanel({super.key, this.data});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: data!.seasonsList!.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
MemberSeasonsList item = data!.seasonsList![index];
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 12, right: 4),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 12, left: 4),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
item.meta!.name!,
|
|
||||||
maxLines: 1,
|
|
||||||
style: Theme.of(context).textTheme.titleSmall!,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
PBadge(
|
|
||||||
stack: 'relative',
|
|
||||||
size: 'small',
|
|
||||||
text: item.meta!.total.toString(),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: '前往',
|
|
||||||
onPressed: () => Get.toNamed(
|
|
||||||
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
LayoutBuilder(
|
|
||||||
builder: (context, boxConstraints) {
|
|
||||||
return GridView.builder(
|
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
|
||||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
|
||||||
childAspectRatio: 0.94,
|
|
||||||
),
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: item.archives!.length,
|
|
||||||
itemBuilder: (context, i) {
|
|
||||||
return MemberSeasonsItem(seasonItem: item.archives![i]);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/http/member.dart';
|
|
||||||
import 'package:PiliPlus/models/member/archive.dart';
|
|
||||||
|
|
||||||
class MemberArchiveController extends GetxController {
|
|
||||||
final ScrollController scrollController = ScrollController();
|
|
||||||
late int mid;
|
|
||||||
late String wwebid;
|
|
||||||
int pn = 1;
|
|
||||||
int count = 0;
|
|
||||||
RxMap<String, String> currentOrder = <String, String>{}.obs;
|
|
||||||
static const List<Map<String, String>> orderList = [
|
|
||||||
{'type': 'pubdate', 'label': '最新发布'},
|
|
||||||
{'type': 'click', 'label': '最多播放'},
|
|
||||||
{'type': 'stow', 'label': '最多收藏'},
|
|
||||||
];
|
|
||||||
RxList<VListItemModel> archivesList = <VListItemModel>[].obs;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onInit() {
|
|
||||||
super.onInit();
|
|
||||||
mid = int.parse(Get.parameters['mid']!);
|
|
||||||
wwebid = Get.parameters['wwebid']!;
|
|
||||||
currentOrder.value = orderList.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户投稿
|
|
||||||
Future getMemberArchive(type) async {
|
|
||||||
if (type == 'init') {
|
|
||||||
pn = 1;
|
|
||||||
}
|
|
||||||
var res = await MemberHttp.memberArchive(
|
|
||||||
mid: mid,
|
|
||||||
pn: pn,
|
|
||||||
order: currentOrder['type']!,
|
|
||||||
wwebid: wwebid,
|
|
||||||
);
|
|
||||||
if (res['status']) {
|
|
||||||
if (type == 'init') {
|
|
||||||
archivesList.value = res['data'].list.vlist;
|
|
||||||
} else if (type == 'onLoad') {
|
|
||||||
archivesList.addAll(res['data'].list.vlist);
|
|
||||||
}
|
|
||||||
count = res['data'].page['count'];
|
|
||||||
pn += 1;
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast(res['msg']);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleSort() {
|
|
||||||
List<String> typeList = orderList.map((e) => e['type']!).toList();
|
|
||||||
int index = typeList.indexOf(currentOrder['type']!);
|
|
||||||
if (index == orderList.length - 1) {
|
|
||||||
currentOrder.value = orderList.first;
|
|
||||||
} else {
|
|
||||||
currentOrder.value = orderList[index + 1];
|
|
||||||
}
|
|
||||||
getMemberArchive('init');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上拉加载
|
|
||||||
Future onLoad() => getMemberArchive('onLoad');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onClose() {
|
|
||||||
scrollController.dispose();
|
|
||||||
super.onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
library member_archive;
|
|
||||||
|
|
||||||
export './controller.dart';
|
|
||||||
export './view.dart';
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
import 'package:easy_debounce/easy_throttle.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/video_card_h.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
import '../../common/constants.dart';
|
|
||||||
import '../../common/widgets/http_error.dart';
|
|
||||||
import '../../utils/grid.dart';
|
|
||||||
import 'controller.dart';
|
|
||||||
|
|
||||||
class MemberArchivePage extends StatefulWidget {
|
|
||||||
const MemberArchivePage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MemberArchivePage> createState() => _MemberArchivePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MemberArchivePageState extends State<MemberArchivePage> {
|
|
||||||
late MemberArchiveController _memberArchivesController;
|
|
||||||
late Future _futureBuilderFuture;
|
|
||||||
late int mid;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_memberArchivesController.scrollController.removeListener(listener);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
mid = int.parse(Get.parameters['mid']!);
|
|
||||||
final String heroTag = Utils.makeHeroTag(mid);
|
|
||||||
_memberArchivesController =
|
|
||||||
Get.put(MemberArchiveController(), tag: heroTag);
|
|
||||||
_futureBuilderFuture = _memberArchivesController.getMemberArchive('init');
|
|
||||||
_memberArchivesController.scrollController.addListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void listener() {
|
|
||||||
if (_memberArchivesController.scrollController.position.pixels >=
|
|
||||||
_memberArchivesController.scrollController.position.maxScrollExtent -
|
|
||||||
200) {
|
|
||||||
EasyThrottle.throttle(
|
|
||||||
'member_archives', const Duration(milliseconds: 500), () {
|
|
||||||
_memberArchivesController.onLoad();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Ta的投稿'),
|
|
||||||
actions: [
|
|
||||||
Obx(
|
|
||||||
() => TextButton.icon(
|
|
||||||
icon: const Icon(Icons.sort, size: 20),
|
|
||||||
onPressed: _memberArchivesController.toggleSort,
|
|
||||||
label: Text(_memberArchivesController.currentOrder['label']!),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: CustomScrollView(
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
controller: _memberArchivesController.scrollController,
|
|
||||||
slivers: [
|
|
||||||
SliverPadding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
|
||||||
sliver: FutureBuilder(
|
|
||||||
future: _futureBuilderFuture,
|
|
||||||
builder: (BuildContext context, snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
|
||||||
if (snapshot.data != null) {
|
|
||||||
// TODO: refactor
|
|
||||||
if (snapshot.data is! Map) {
|
|
||||||
return HttpError(
|
|
||||||
callback: () => setState(() {
|
|
||||||
_futureBuilderFuture = _memberArchivesController
|
|
||||||
.getMemberArchive('init');
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Map data = snapshot.data as Map;
|
|
||||||
final list = _memberArchivesController.archivesList;
|
|
||||||
if (data['status']) {
|
|
||||||
return Obx(
|
|
||||||
() => list.isNotEmpty
|
|
||||||
? SliverGrid(
|
|
||||||
gridDelegate:
|
|
||||||
SliverGridDelegateWithExtentAndRatio(
|
|
||||||
mainAxisSpacing: StyleString.safeSpace,
|
|
||||||
crossAxisSpacing: StyleString.safeSpace,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio:
|
|
||||||
StyleString.aspectRatio * 2.4,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(BuildContext context, index) {
|
|
||||||
return VideoCardH(
|
|
||||||
videoItem: list[index],
|
|
||||||
showOwner: false,
|
|
||||||
showPubdate: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
childCount: list.length,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SliverToBoxAdapter(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return HttpError(
|
|
||||||
errMsg: snapshot.data['msg'],
|
|
||||||
callback: () {},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return HttpError(
|
|
||||||
errMsg: "投稿页出现错误",
|
|
||||||
callback: () {},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return const SliverToBoxAdapter();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,13 +22,15 @@ class MemberSearchController extends GetxController
|
|||||||
int archivePn = 1;
|
int archivePn = 1;
|
||||||
RxInt archiveCount = (-1).obs;
|
RxInt archiveCount = (-1).obs;
|
||||||
bool isEndArchive = false;
|
bool isEndArchive = false;
|
||||||
Rx<LoadingState> archiveState = LoadingState.loading().obs;
|
Rx<LoadingState<List<VListItemModel>?>> archiveState =
|
||||||
|
LoadingState<List<VListItemModel>?>.loading().obs;
|
||||||
|
|
||||||
String offset = '';
|
String offset = '';
|
||||||
int dynamicPn = 1;
|
int dynamicPn = 1;
|
||||||
RxInt dynamicCount = (-1).obs;
|
RxInt dynamicCount = (-1).obs;
|
||||||
bool isEndDynamic = false;
|
bool isEndDynamic = false;
|
||||||
Rx<LoadingState> dynamicState = LoadingState.loading().obs;
|
Rx<LoadingState<List<DynamicItemModel>?>> dynamicState =
|
||||||
|
LoadingState<List<DynamicItemModel>?>.loading().obs;
|
||||||
|
|
||||||
dynamic wwebid;
|
dynamic wwebid;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
|||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/common/widgets/video_card_h.dart';
|
import 'package:PiliPlus/common/widgets/video_card_h.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/member/archive.dart';
|
||||||
import 'package:PiliPlus/pages/member_search/controller.dart';
|
import 'package:PiliPlus/pages/member_search/controller.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
@@ -29,10 +30,11 @@ class _SearchArchiveState extends State<SearchArchive>
|
|||||||
return Obx(() => _buildBody(context, widget.ctr.archiveState.value));
|
return Obx(() => _buildBody(context, widget.ctr.archiveState.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(BuildContext context, LoadingState loadingState) {
|
Widget _buildBody(
|
||||||
|
BuildContext context, LoadingState<List<VListItemModel>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => loadingWidget,
|
Loading() => loadingWidget,
|
||||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? refreshIndicator(
|
? refreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await widget.ctr.refreshArchive();
|
await widget.ctr.refreshArchive();
|
||||||
@@ -46,24 +48,20 @@ class _SearchArchiveState extends State<SearchArchive>
|
|||||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
EasyThrottle.throttle('searchArchives',
|
EasyThrottle.throttle('searchArchives',
|
||||||
const Duration(milliseconds: 500), () {
|
const Duration(milliseconds: 500), () {
|
||||||
widget.ctr.searchArchives(false);
|
widget.ctr.searchArchives(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: loadingState.response[index],
|
videoItem: loadingState.response![index],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: loadingState.response.length,
|
childCount: loadingState.response!.length,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
|
|||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart';
|
import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart';
|
||||||
import 'package:PiliPlus/pages/member_search/controller.dart';
|
import 'package:PiliPlus/pages/member_search/controller.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
@@ -31,12 +32,13 @@ class _SearchDynamicState extends State<SearchDynamic>
|
|||||||
return Obx(() => _buildBody(context, widget.ctr.dynamicState.value));
|
return Obx(() => _buildBody(context, widget.ctr.dynamicState.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(BuildContext context, LoadingState loadingState) {
|
Widget _buildBody(BuildContext context,
|
||||||
|
LoadingState<List<DynamicItemModel>?> loadingState) {
|
||||||
bool dynamicsWaterfallFlow = GStorage.setting
|
bool dynamicsWaterfallFlow = GStorage.setting
|
||||||
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => loadingWidget,
|
Loading() => loadingWidget,
|
||||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? refreshIndicator(
|
? refreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await widget.ctr.refreshDynamic();
|
await widget.ctr.refreshDynamic();
|
||||||
@@ -54,13 +56,13 @@ class _SearchDynamicState extends State<SearchDynamic>
|
|||||||
crossAxisSpacing: StyleString.safeSpace,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
mainAxisSpacing: StyleString.safeSpace,
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
lastChildLayoutTypeBuilder: (index) {
|
lastChildLayoutTypeBuilder: (index) {
|
||||||
if (index == loadingState.response.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
EasyThrottle.throttle('member_dynamics',
|
EasyThrottle.throttle('member_dynamics',
|
||||||
const Duration(milliseconds: 1000), () {
|
const Duration(milliseconds: 1000), () {
|
||||||
widget.ctr.searchDynamic(false);
|
widget.ctr.searchDynamic(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return index == loadingState.response.length
|
return index == loadingState.response!.length
|
||||||
? LastChildLayoutType.foot
|
? LastChildLayoutType.foot
|
||||||
: LastChildLayoutType.none;
|
: LastChildLayoutType.none;
|
||||||
},
|
},
|
||||||
@@ -77,7 +79,7 @@ class _SearchDynamicState extends State<SearchDynamic>
|
|||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index ==
|
if (index ==
|
||||||
loadingState.response.length - 1) {
|
loadingState.response!.length - 1) {
|
||||||
EasyThrottle.throttle('member_dynamics',
|
EasyThrottle.throttle('member_dynamics',
|
||||||
const Duration(milliseconds: 1000),
|
const Duration(milliseconds: 1000),
|
||||||
() {
|
() {
|
||||||
@@ -85,10 +87,10 @@ class _SearchDynamicState extends State<SearchDynamic>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return DynamicPanel(
|
return DynamicPanel(
|
||||||
item: loadingState.response[index],
|
item: loadingState.response![index],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: loadingState.response.length,
|
childCount: loadingState.response!.length,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/http/member.dart';
|
|
||||||
import 'package:PiliPlus/models/member/seasons.dart';
|
|
||||||
|
|
||||||
class MemberSeasonsController extends GetxController {
|
|
||||||
final ScrollController scrollController = ScrollController();
|
|
||||||
late int mid;
|
|
||||||
late int seasonId;
|
|
||||||
int pn = 1;
|
|
||||||
int ps = 30;
|
|
||||||
int count = 0;
|
|
||||||
RxList<MemberArchiveItem> seasonsList = <MemberArchiveItem>[].obs;
|
|
||||||
late Map page;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onInit() {
|
|
||||||
super.onInit();
|
|
||||||
mid = int.parse(Get.parameters['mid']!);
|
|
||||||
seasonId = int.parse(Get.parameters['seasonId']!);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取专栏详情
|
|
||||||
Future getSeasonDetail(type) async {
|
|
||||||
if (type == 'onRefresh') {
|
|
||||||
pn = 1;
|
|
||||||
}
|
|
||||||
var res = await MemberHttp.getSeasonDetail(
|
|
||||||
mid: mid,
|
|
||||||
seasonId: seasonId,
|
|
||||||
pn: pn,
|
|
||||||
ps: ps,
|
|
||||||
sortReverse: false,
|
|
||||||
);
|
|
||||||
if (res['status']) {
|
|
||||||
seasonsList.addAll(res['data'].archives);
|
|
||||||
page = res['data'].page;
|
|
||||||
pn += 1;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上拉加载
|
|
||||||
Future onLoad() async {
|
|
||||||
getSeasonDetail('onLoad');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onClose() {
|
|
||||||
scrollController.dispose();
|
|
||||||
super.onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
library member_seasons;
|
|
||||||
|
|
||||||
export 'controller.dart';
|
|
||||||
export 'view.dart';
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
|
||||||
import 'package:PiliPlus/models/member/seasons.dart';
|
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import '../../utils/grid.dart';
|
|
||||||
import 'controller.dart';
|
|
||||||
import 'widgets/item.dart';
|
|
||||||
|
|
||||||
class MemberSeasonsPage extends StatefulWidget {
|
|
||||||
const MemberSeasonsPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MemberSeasonsPage> createState() => _MemberSeasonsPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MemberSeasonsPageState extends State<MemberSeasonsPage> {
|
|
||||||
final MemberSeasonsController _memberSeasonsController =
|
|
||||||
Get.put(MemberSeasonsController());
|
|
||||||
late Future _futureBuilderFuture;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_memberSeasonsController.scrollController.removeListener(listener);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_futureBuilderFuture =
|
|
||||||
_memberSeasonsController.getSeasonDetail('onRefresh');
|
|
||||||
_memberSeasonsController.scrollController.addListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void listener() {
|
|
||||||
if (_memberSeasonsController.scrollController.position.pixels >=
|
|
||||||
_memberSeasonsController.scrollController.position.maxScrollExtent -
|
|
||||||
200) {
|
|
||||||
EasyThrottle.throttle(
|
|
||||||
'member_archives', const Duration(milliseconds: 500), () {
|
|
||||||
_memberSeasonsController.onLoad();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: const Text('Ta的专栏')),
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: StyleString.safeSpace,
|
|
||||||
right: StyleString.safeSpace,
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
controller: _memberSeasonsController.scrollController,
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: _futureBuilderFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
|
||||||
if (snapshot.data != null) {
|
|
||||||
// TODO: refactor
|
|
||||||
if (snapshot.data is! Map) {
|
|
||||||
return HttpError(
|
|
||||||
isSliver: false,
|
|
||||||
callback: () => setState(() {
|
|
||||||
_futureBuilderFuture = _memberSeasonsController
|
|
||||||
.getSeasonDetail('onRefresh');
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Map data = snapshot.data as Map;
|
|
||||||
List<MemberArchiveItem> list =
|
|
||||||
_memberSeasonsController.seasonsList;
|
|
||||||
if (data['status']) {
|
|
||||||
return Obx(
|
|
||||||
() => list.isNotEmpty
|
|
||||||
? LayoutBuilder(
|
|
||||||
builder: (context, boxConstraints) {
|
|
||||||
return GridView.builder(
|
|
||||||
gridDelegate:
|
|
||||||
SliverGridDelegateWithExtentAndRatio(
|
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
|
||||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
|
||||||
childAspectRatio: 0.94,
|
|
||||||
),
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: _memberSeasonsController
|
|
||||||
.seasonsList.length,
|
|
||||||
itemBuilder: (context, i) {
|
|
||||||
return MemberSeasonsItem(
|
|
||||||
seasonItem: _memberSeasonsController
|
|
||||||
.seasonsList[i],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: const SizedBox(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
|
||||||
import 'package:PiliPlus/models/member/seasons.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:PiliPlus/http/search.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
|
|
||||||
class MemberSeasonsItem extends StatelessWidget {
|
|
||||||
final MemberArchiveItem seasonItem;
|
|
||||||
|
|
||||||
const MemberSeasonsItem({
|
|
||||||
super.key,
|
|
||||||
required this.seasonItem,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Card(
|
|
||||||
elevation: 0,
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () async {
|
|
||||||
int cid =
|
|
||||||
await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid);
|
|
||||||
Utils.toViewPage(
|
|
||||||
'bvid=${seasonItem.bvid}&cid=$cid',
|
|
||||||
arguments: {
|
|
||||||
'videoItem': seasonItem,
|
|
||||||
'heroTag': Utils.makeHeroTag(seasonItem.aid)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: StyleString.aspectRatio,
|
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
src: seasonItem.pic,
|
|
||||||
width: maxWidth,
|
|
||||||
height: maxHeight,
|
|
||||||
),
|
|
||||||
if (seasonItem.pubdate != null)
|
|
||||||
PBadge(
|
|
||||||
bottom: 6,
|
|
||||||
right: 6,
|
|
||||||
type: 'gray',
|
|
||||||
text: Utils.customStampStr(
|
|
||||||
timestamp: seasonItem.pubdate, date: 'YY-MM-DD'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(5, 6, 0, 0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
seasonItem.title!,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
StatView(
|
|
||||||
context: context,
|
|
||||||
value: Utils.numFormat(seasonItem.view!),
|
|
||||||
theme: 'gray',
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Text(
|
|
||||||
Utils.customStampStr(
|
|
||||||
timestamp: seasonItem.pubdate, date: 'YY-MM-DD'),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 11,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -57,11 +57,7 @@ class _ZonePageState extends CommonPageState<ZonePage, ZoneController>
|
|||||||
|
|
||||||
Widget _buildSkeleton() {
|
Widget _buildSkeleton() {
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -76,11 +72,7 @@ class _ZonePageState extends CommonPageState<ZonePage, ZoneController>
|
|||||||
Loading() => _buildSkeleton(),
|
Loading() => _buildSkeleton(),
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? SliverGrid(
|
? SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
|
padding: MediaQuery.paddingOf(context).copyWith(top: 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// 搜索建议
|
// 搜索建议
|
||||||
@@ -89,10 +90,16 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (_searchController.enableHotKey)
|
if (_searchController.enableHotKey)
|
||||||
Expanded(child: hotSearch()),
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
hotSearch(),
|
||||||
|
if (_searchController.enableSearchRcmd)
|
||||||
|
hotSearch(false)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(child: _history()),
|
Expanded(child: _history()),
|
||||||
if (_searchController.enableSearchRcmd)
|
|
||||||
Expanded(child: hotSearch(false)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -143,7 +150,7 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
.copyWith(height: 1, fontWeight: FontWeight.bold),
|
.copyWith(height: 1, fontWeight: FontWeight.bold),
|
||||||
);
|
);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 25, 4, 25),
|
padding: EdgeInsets.fromLTRB(10, isHot ? 25 : 4, 4, 25),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -67,19 +67,15 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverGrid(
|
SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: widget.searchType == SearchType.media_bangumi ||
|
||||||
mainAxisSpacing: 2,
|
widget.searchType == SearchType.media_ft
|
||||||
maxCrossAxisExtent: (widget.searchType == SearchType.video ||
|
? SliverGridDelegateWithExtentAndRatio(
|
||||||
widget.searchType == SearchType.article
|
mainAxisSpacing: 2,
|
||||||
? Grid.mediumCardWidth
|
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||||
: Grid.smallCardWidth) *
|
childAspectRatio: StyleString.aspectRatio * 1.5,
|
||||||
2,
|
minHeight: MediaQuery.textScalerOf(context).scale(155),
|
||||||
childAspectRatio: StyleString.aspectRatio *
|
)
|
||||||
(widget.searchType == SearchType.media_bangumi ||
|
: Grid.videoCardHDelegate(context),
|
||||||
widget.searchType == SearchType.media_ft
|
|
||||||
? 1.5
|
|
||||||
: 2.2),
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
switch (widget.searchType) {
|
switch (widget.searchType) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'dart:math';
|
|||||||
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
||||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image_save.dart';
|
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
||||||
@@ -82,7 +81,6 @@ Widget searchArticlePanel(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
switch (loadingState) {
|
switch (loadingState) {
|
||||||
Loading() => errorWidget(),
|
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? SliverPadding(
|
? SliverPadding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
@@ -90,11 +88,7 @@ Widget searchArticlePanel(
|
|||||||
MediaQuery.of(context).padding.bottom,
|
MediaQuery.of(context).padding.bottom,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import '../../../utils/grid.dart';
|
|||||||
Widget searchLivePanel(
|
Widget searchLivePanel(
|
||||||
BuildContext context, ctr, LoadingState<List<dynamic>?> loadingState) {
|
BuildContext context, ctr, LoadingState<List<dynamic>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => loadingWidget,
|
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? GridView.builder(
|
? GridView.builder(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ Widget searchBangumiPanel(
|
|||||||
context, ctr, LoadingState<List<dynamic>?> loadingState) {
|
context, ctr, LoadingState<List<dynamic>?> loadingState) {
|
||||||
late TextStyle style = TextStyle(fontSize: 13);
|
late TextStyle style = TextStyle(fontSize: 13);
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => loadingWidget,
|
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? CustomScrollView(
|
? CustomScrollView(
|
||||||
controller: ctr.scrollController,
|
controller: ctr.scrollController,
|
||||||
@@ -146,10 +145,10 @@ Widget searchBangumiPanel(
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: errorWidget(
|
: scrollErrorWidget(
|
||||||
callback: ctr.onReload,
|
callback: ctr.onReload,
|
||||||
),
|
),
|
||||||
Error() => errorWidget(
|
Error() => scrollErrorWidget(
|
||||||
errMsg: loadingState.errMsg,
|
errMsg: loadingState.errMsg,
|
||||||
callback: ctr.onReload,
|
callback: ctr.onReload,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
||||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
||||||
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
import 'package:PiliPlus/pages/search_panel/controller.dart';
|
||||||
@@ -80,7 +79,6 @@ Widget searchUserPanel(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
switch (loadingState) {
|
switch (loadingState) {
|
||||||
Loading() => errorWidget(),
|
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? SliverPadding(
|
? SliverPadding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
@@ -89,7 +87,7 @@ Widget searchUserPanel(
|
|||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||||
mainAxisExtent: 56,
|
mainAxisExtent: 66,
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
||||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -13,7 +12,6 @@ import 'package:PiliPlus/models/common/search_type.dart';
|
|||||||
import 'package:PiliPlus/pages/search_panel/index.dart';
|
import 'package:PiliPlus/pages/search_panel/index.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import '../../../common/constants.dart';
|
|
||||||
import '../../../utils/grid.dart';
|
import '../../../utils/grid.dart';
|
||||||
|
|
||||||
Widget searchVideoPanel(
|
Widget searchVideoPanel(
|
||||||
@@ -92,18 +90,13 @@ Widget searchVideoPanel(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
switch (loadingState) {
|
switch (loadingState) {
|
||||||
Loading() => errorWidget(),
|
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? SliverPadding(
|
? SliverPadding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import 'package:PiliPlus/models/video/play/subtitle.dart';
|
|||||||
import 'package:PiliPlus/pages/home/controller.dart';
|
import 'package:PiliPlus/pages/home/controller.dart';
|
||||||
import 'package:PiliPlus/pages/hot/controller.dart';
|
import 'package:PiliPlus/pages/hot/controller.dart';
|
||||||
import 'package:PiliPlus/pages/main/controller.dart';
|
import 'package:PiliPlus/pages/main/controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
import 'package:PiliPlus/pages/member/controller.dart';
|
||||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||||
import 'package:PiliPlus/pages/rcmd/controller.dart';
|
import 'package:PiliPlus/pages/rcmd/controller.dart';
|
||||||
import 'package:PiliPlus/pages/setting/pages/color_select.dart';
|
import 'package:PiliPlus/pages/setting/pages/color_select.dart';
|
||||||
@@ -247,7 +247,7 @@ List<SettingsModel> get styleSettings => [
|
|||||||
builder: (context) {
|
builder: (context) {
|
||||||
return SlideDialog(
|
return SlideDialog(
|
||||||
title: '中卡最大列宽度(默认280dp)',
|
title: '中卡最大列宽度(默认280dp)',
|
||||||
value: GStorage.mediumCardWidth,
|
value: GStorage.smallCardWidth,
|
||||||
min: 150.0,
|
min: 150.0,
|
||||||
max: 500.0,
|
max: 500.0,
|
||||||
divisions: 35,
|
divisions: 35,
|
||||||
@@ -255,7 +255,7 @@ List<SettingsModel> get styleSettings => [
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
await GStorage.setting.put(SettingBoxKey.mediumCardWidth, result);
|
await GStorage.setting.put(SettingBoxKey.smallCardWidth, result);
|
||||||
SmartDialog.showToast('重启生效');
|
SmartDialog.showToast('重启生效');
|
||||||
setState();
|
setState();
|
||||||
}
|
}
|
||||||
@@ -263,7 +263,7 @@ List<SettingsModel> get styleSettings => [
|
|||||||
leading: const Icon(Icons.calendar_view_week_outlined),
|
leading: const Icon(Icons.calendar_view_week_outlined),
|
||||||
title: '中卡列表宽度(dp)限制',
|
title: '中卡列表宽度(dp)限制',
|
||||||
getSubtitle: () =>
|
getSubtitle: () =>
|
||||||
'当前:${GStorage.mediumCardWidth.toInt()}dp,屏幕宽度:${MediaQuery.of(Get.context!).size.width.toPrecision(2)}dp。宽度越小列数越多。',
|
'当前:${GStorage.smallCardWidth.toInt()}dp,屏幕宽度:${MediaQuery.of(Get.context!).size.width.toPrecision(2)}dp。宽度越小列数越多。',
|
||||||
),
|
),
|
||||||
SettingsModel(
|
SettingsModel(
|
||||||
settingsType: SettingsType.sw1tch,
|
settingsType: SettingsType.sw1tch,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
@@ -45,11 +44,7 @@ class _SubPageState extends State<SubPage> {
|
|||||||
Widget _buildBody(LoadingState<List<SubFolderItemData>?> loadingState) {
|
Widget _buildBody(LoadingState<List<SubFolderItemData>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) => const VideoCardHSkeleton(),
|
(context, index) => const VideoCardHSkeleton(),
|
||||||
childCount: 10,
|
childCount: 10,
|
||||||
@@ -57,11 +52,7 @@ class _SubPageState extends State<SubPage> {
|
|||||||
),
|
),
|
||||||
Success() => loadingState.response?.isNotEmpty == true
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
? SliverGrid(
|
? SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount: loadingState.response!.length,
|
childCount: loadingState.response!.length,
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/models/user/sub_detail.dart';
|
import 'package:PiliPlus/models/user/sub_detail.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
@@ -227,13 +226,7 @@ class _SubDetailPageState extends State<SubDetailPage> {
|
|||||||
MediaQuery.paddingOf(context).bottom + 80,
|
MediaQuery.paddingOf(context).bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
SliverGridDelegateWithExtentAndRatio(
|
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio:
|
|
||||||
StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount: subList.length,
|
childCount: subList.length,
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
@@ -258,11 +251,7 @@ class _SubDetailPageState extends State<SubDetailPage> {
|
|||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) => const VideoCardHSkeleton(),
|
(context, index) => const VideoCardHSkeleton(),
|
||||||
childCount: 10,
|
childCount: 10,
|
||||||
|
|||||||
@@ -75,15 +75,6 @@ class SubVideoCardH extends StatelessWidget {
|
|||||||
bottom: 6.0,
|
bottom: 6.0,
|
||||||
type: 'gray',
|
type: 'gray',
|
||||||
),
|
),
|
||||||
// if (videoItem.ogv != null) ...[
|
|
||||||
// PBadge(
|
|
||||||
// text: videoItem.ogv['type_name'],
|
|
||||||
// top: 6.0,
|
|
||||||
// right: 6.0,
|
|
||||||
// bottom: null,
|
|
||||||
// left: null,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -107,20 +98,23 @@ class SubVideoCardH extends StatelessWidget {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
'${videoItem.title}',
|
child: Text(
|
||||||
textAlign: TextAlign.start,
|
'${videoItem.title}',
|
||||||
style: const TextStyle(
|
textAlign: TextAlign.start,
|
||||||
letterSpacing: 0.3,
|
style: const TextStyle(
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
Text(
|
Text(
|
||||||
Utils.dateFormat(videoItem.pubtime),
|
Utils.dateFormat(videoItem.pubtime),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11, color: Theme.of(context).colorScheme.outline),
|
fontSize: 12,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 2),
|
padding: const EdgeInsets.only(top: 2),
|
||||||
|
|||||||
@@ -612,14 +612,13 @@ class _VideoInfoState extends State<VideoInfo> {
|
|||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
context: context,
|
context: context,
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
value: Utils.numFormat(!widget.loadingStatus
|
value: Utils.numFormat(!widget.loadingStatus
|
||||||
? videoDetail.stat?.view ?? '-'
|
? videoDetail.stat?.view ?? '-'
|
||||||
: videoItem['stat']?.view ?? '-'),
|
: videoItem['stat']?.view ?? '-'),
|
||||||
size: 'medium',
|
|
||||||
textColor: t.colorScheme.outline,
|
textColor: t.colorScheme.outline,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
@@ -629,7 +628,6 @@ class _VideoInfoState extends State<VideoInfo> {
|
|||||||
value: Utils.numFormat(!widget.loadingStatus
|
value: Utils.numFormat(!widget.loadingStatus
|
||||||
? videoDetail.stat?.danmaku ?? '-'
|
? videoDetail.stat?.danmaku ?? '-'
|
||||||
: videoItem['stat']?.danmu ?? '-'),
|
: videoItem['stat']?.danmu ?? '-'),
|
||||||
size: 'medium',
|
|
||||||
textColor: t.colorScheme.outline,
|
textColor: t.colorScheme.outline,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
|
|||||||
@@ -1,211 +0,0 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
|
|
||||||
@Deprecated('deprecated')
|
|
||||||
class IntroDetail extends StatelessWidget {
|
|
||||||
const IntroDetail({
|
|
||||||
super.key,
|
|
||||||
this.videoDetail,
|
|
||||||
this.videoTags,
|
|
||||||
});
|
|
||||||
final dynamic videoDetail;
|
|
||||||
final dynamic videoTags;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Material(
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
InkWell(
|
|
||||||
onTap: () => Get.back(),
|
|
||||||
child: Container(
|
|
||||||
height: 35,
|
|
||||||
padding: const EdgeInsets.only(bottom: 2),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: 32,
|
|
||||||
height: 3,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
borderRadius:
|
|
||||||
const BorderRadius.all(Radius.circular(3))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
SelectableText(
|
|
||||||
videoDetail!.title,
|
|
||||||
style: const TextStyle(fontSize: 16),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
StatView(
|
|
||||||
context: context,
|
|
||||||
theme: 'gray',
|
|
||||||
value: Utils.numFormat(videoDetail!.stat!.view),
|
|
||||||
size: 'medium',
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
StatDanMu(
|
|
||||||
context: context,
|
|
||||||
theme: 'gray',
|
|
||||||
value: Utils.numFormat(videoDetail!.stat!.danmu),
|
|
||||||
size: 'medium',
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
Utils.dateFormat(videoDetail!.pubdate,
|
|
||||||
formatType: 'detail'),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (videoTags is List && videoTags.isNotEmpty) ...[
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Wrap(
|
|
||||||
spacing: 8,
|
|
||||||
runSpacing: 8,
|
|
||||||
children: (videoTags as List)
|
|
||||||
.map(
|
|
||||||
(item) => SearchText(
|
|
||||||
fontSize: 13,
|
|
||||||
text: item['tag_name'],
|
|
||||||
onTap: (_) => Get.toNamed('/searchResult',
|
|
||||||
parameters: {'keyword': item['tag_name']}),
|
|
||||||
onLongPress: (_) =>
|
|
||||||
Utils.copyText(item['tag_name']),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: SelectableRegion(
|
|
||||||
focusNode: FocusNode(),
|
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
videoDetail!.bvid!,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text.rich(
|
|
||||||
style: const TextStyle(
|
|
||||||
height: 1.4,
|
|
||||||
// fontSize: 13,
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
buildContent(context, videoDetail!),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 100),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
InlineSpan buildContent(BuildContext context, content) {
|
|
||||||
final List descV2 = content.descV2;
|
|
||||||
// type
|
|
||||||
// 1 普通文本
|
|
||||||
// 2 @用户
|
|
||||||
final List<TextSpan> spanChildren = List.generate(descV2.length, (index) {
|
|
||||||
final currentDesc = descV2[index];
|
|
||||||
switch (currentDesc.type) {
|
|
||||||
case 1:
|
|
||||||
final List<InlineSpan> spanChildren = <InlineSpan>[];
|
|
||||||
final RegExp urlRegExp = RegExp(Constants.urlPattern);
|
|
||||||
final Iterable<Match> matches =
|
|
||||||
urlRegExp.allMatches(currentDesc.rawText);
|
|
||||||
|
|
||||||
int previousEndIndex = 0;
|
|
||||||
for (final Match match in matches) {
|
|
||||||
if (match.start > previousEndIndex) {
|
|
||||||
spanChildren.add(TextSpan(
|
|
||||||
text: currentDesc.rawText
|
|
||||||
.substring(previousEndIndex, match.start)));
|
|
||||||
}
|
|
||||||
spanChildren.add(
|
|
||||||
TextSpan(
|
|
||||||
text: match.group(0),
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.primary), // 设置颜色为蓝色
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () {
|
|
||||||
// 处理点击事件
|
|
||||||
try {
|
|
||||||
Utils.handleWebview(match.group(0)!);
|
|
||||||
} catch (err) {
|
|
||||||
SmartDialog.showToast(err.toString());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
previousEndIndex = match.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previousEndIndex < currentDesc.rawText.length) {
|
|
||||||
spanChildren.add(TextSpan(
|
|
||||||
text: currentDesc.rawText.substring(previousEndIndex)));
|
|
||||||
}
|
|
||||||
|
|
||||||
final TextSpan result = TextSpan(children: spanChildren);
|
|
||||||
return result;
|
|
||||||
case 2:
|
|
||||||
final Color colorSchemePrimary =
|
|
||||||
Theme.of(context).colorScheme.primary;
|
|
||||||
final String heroTag = Utils.makeHeroTag(currentDesc.bizId);
|
|
||||||
return TextSpan(
|
|
||||||
text: '@${currentDesc.rawText}',
|
|
||||||
style: TextStyle(color: colorSchemePrimary),
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () {
|
|
||||||
Get.toNamed(
|
|
||||||
'/member?mid=${currentDesc.bizId}',
|
|
||||||
arguments: {'face': '', 'heroTag': heroTag},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return const TextSpan();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return TextSpan(children: spanChildren);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import 'package:PiliPlus/models/member/info.dart';
|
|||||||
import 'package:PiliPlus/models/space_archive/data.dart';
|
import 'package:PiliPlus/models/space_archive/data.dart';
|
||||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart'
|
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||||
show ContributeType;
|
show ContributeType;
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
||||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||||
@@ -191,11 +190,7 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
|
|||||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index == loadingState.response.length - 1 &&
|
if (index == loadingState.response.length - 1 &&
|
||||||
|
|||||||
@@ -36,11 +36,7 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
|||||||
Widget _buildBody(LoadingState<List<HotVideoItemModel>?> loadingState) {
|
Widget _buildBody(LoadingState<List<HotVideoItemModel>?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
@@ -54,11 +50,7 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
|||||||
bottom: MediaQuery.of(context).padding.bottom,
|
bottom: MediaQuery.of(context).padding.bottom,
|
||||||
),
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
mainAxisSpacing: 2,
|
|
||||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
|
||||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
|
||||||
),
|
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: loadingState.response![index],
|
videoItem: loadingState.response![index],
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,153 +0,0 @@
|
|||||||
import 'package:PiliPlus/utils/extension.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:PiliPlus/http/reply.dart';
|
|
||||||
import 'package:PiliPlus/models/common/reply_type.dart';
|
|
||||||
import 'package:PiliPlus/models/video/reply/item.dart';
|
|
||||||
import 'package:PiliPlus/utils/feed_back.dart';
|
|
||||||
|
|
||||||
class ZanButton extends StatefulWidget {
|
|
||||||
const ZanButton({
|
|
||||||
super.key,
|
|
||||||
this.replyItem,
|
|
||||||
this.replyType,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ReplyItemModel? replyItem;
|
|
||||||
final ReplyType? replyType;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ZanButton> createState() => _ZanButtonState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ZanButtonState extends State<ZanButton> {
|
|
||||||
Future onHateReply() async {
|
|
||||||
feedBack();
|
|
||||||
// SmartDialog.showLoading(msg: 'piliplus ...');
|
|
||||||
final ReplyItemModel replyItem = widget.replyItem!;
|
|
||||||
final int oid = replyItem.oid!;
|
|
||||||
final int rpid = replyItem.rpid!;
|
|
||||||
// 1 已点赞 2 不喜欢 0 未操作
|
|
||||||
final int action = replyItem.action != 2 ? 2 : 0;
|
|
||||||
final res = await ReplyHttp.hateReply(
|
|
||||||
type: widget.replyType!.index,
|
|
||||||
action: action == 2 ? 1 : 0,
|
|
||||||
oid: oid,
|
|
||||||
rpid: rpid,
|
|
||||||
);
|
|
||||||
// SmartDialog.dismiss();
|
|
||||||
if (res['status']) {
|
|
||||||
SmartDialog.showToast(replyItem.action != 2 ? '点踩成功' : '取消踩');
|
|
||||||
if (action == 2) {
|
|
||||||
if (replyItem.action == 1) {
|
|
||||||
replyItem.like = replyItem.like! - 1;
|
|
||||||
}
|
|
||||||
replyItem.action = 2;
|
|
||||||
} else {
|
|
||||||
// replyItem.like = replyItem.like! - 1;
|
|
||||||
replyItem.action = 0;
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast(res['msg']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 评论点赞
|
|
||||||
Future onLikeReply() async {
|
|
||||||
feedBack();
|
|
||||||
// SmartDialog.showLoading(msg: 'piliplus ...');
|
|
||||||
final ReplyItemModel replyItem = widget.replyItem!;
|
|
||||||
final int oid = replyItem.oid!;
|
|
||||||
final int rpid = replyItem.rpid!;
|
|
||||||
// 1 已点赞 2 不喜欢 0 未操作
|
|
||||||
final int action = replyItem.action != 1 ? 1 : 0;
|
|
||||||
final res = await ReplyHttp.likeReply(
|
|
||||||
type: widget.replyType!.index, oid: oid, rpid: rpid, action: action);
|
|
||||||
// SmartDialog.dismiss();
|
|
||||||
if (res['status']) {
|
|
||||||
SmartDialog.showToast(replyItem.action != 1 ? '点赞成功' : '取消赞');
|
|
||||||
if (action == 1) {
|
|
||||||
replyItem.like = replyItem.like! + 1;
|
|
||||||
replyItem.action = 1;
|
|
||||||
} else {
|
|
||||||
replyItem.like = replyItem.like! - 1;
|
|
||||||
replyItem.action = 0;
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast(res['msg']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isProcessing = false;
|
|
||||||
void handleState(Future Function() action) async {
|
|
||||||
if (isProcessing.not) {
|
|
||||||
isProcessing = true;
|
|
||||||
await action();
|
|
||||||
isProcessing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final ThemeData t = Theme.of(context);
|
|
||||||
final Color color = t.colorScheme.outline;
|
|
||||||
final Color primary = t.colorScheme.primary;
|
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
height: 32,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => handleState(onHateReply),
|
|
||||||
child: Icon(
|
|
||||||
widget.replyItem!.action == 2
|
|
||||||
? FontAwesomeIcons.solidThumbsDown
|
|
||||||
: FontAwesomeIcons.thumbsDown,
|
|
||||||
size: 16,
|
|
||||||
color: widget.replyItem!.action == 2 ? primary : color,
|
|
||||||
semanticLabel: widget.replyItem!.action == 2 ? '已踩' : '点踩',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 32,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => handleState(onLikeReply),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
widget.replyItem!.action == 1
|
|
||||||
? FontAwesomeIcons.solidThumbsUp
|
|
||||||
: FontAwesomeIcons.thumbsUp,
|
|
||||||
size: 16,
|
|
||||||
color: widget.replyItem!.action == 1 ? primary : color,
|
|
||||||
semanticLabel: widget.replyItem!.action == 1 ? '已赞' : '点赞',
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 400),
|
|
||||||
transitionBuilder:
|
|
||||||
(Widget child, Animation<double> animation) {
|
|
||||||
return ScaleTransition(scale: animation, child: child);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
Utils.numFormat(widget.replyItem!.like),
|
|
||||||
key: ValueKey<int>(widget.replyItem!.like!),
|
|
||||||
style: TextStyle(
|
|
||||||
color: widget.replyItem!.action == 1 ? primary : color,
|
|
||||||
fontSize: t.textTheme.labelSmall!.fontSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user