mod: split some pages

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-01-24 17:50:43 +08:00
parent f3b1c416b9
commit be20b1bd73
18 changed files with 1472 additions and 1461 deletions

View File

@@ -9,6 +9,11 @@ import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/models/common/sponsor_block/action_type.dart';
import 'package:PiliPlus/models/common/sponsor_block/post_segment_model.dart';
import 'package:PiliPlus/models/common/sponsor_block/segment_model.dart';
import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart';
import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
import 'package:PiliPlus/models/video/later.dart';
import 'package:PiliPlus/models/video/play/subtitle.dart';
import 'package:PiliPlus/models/video_detail_res.dart';
@@ -43,114 +48,6 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart
import '../../../utils/id_utils.dart';
import 'widgets/header_control.dart';
enum SegmentType {
sponsor,
selfpromo,
interaction,
intro,
outro,
preview,
music_offtopic,
poi_highlight,
filler,
exclusive_access
}
extension SegmentTypeExt on SegmentType {
/// from https://github.com/hanydd/BilibiliSponsorBlock/blob/master/public/_locales/zh_CN/messages.json
String get title => [
'赞助广告', //sponsor
'无偿/自我推广', //selfpromo
'三连/订阅提醒', //interaction
'过场/开场动画', //intro
'鸣谢/结束画面', //outro
'回顾/概要', //preview
'音乐:非音乐部分', //music_offtopic
'精彩时刻/重点', //poi_highlight
'离题闲聊/玩笑', //filler
'柔性推广/品牌合作', //exclusive_access
][index];
String get shortTitle => [
'赞助广告', //sponsor
'推广', //selfpromo
'订阅提醒', //interaction
'开场', //intro
'片尾', //outro
'预览', //preview
'非音乐', //music_offtopic
'精彩时刻', //poi_highlight
'闲聊', //filler
'品牌合作', //exclusive_access
][index];
String get description => [
'付费推广、付费推荐和直接广告。不是自我推广或免费提及他们喜欢的商品/创作者/网站/产品。', //sponsor
'类似于 “赞助广告” ,但无报酬或是自我推广。包括有关商品、捐赠的部分或合作者的信息。', //selfpromo
'视频中间简短提醒观众来一键三连或关注。 如果片段较长,或是有具体内容,则应分类为自我推广。', //interaction
'没有实际内容的间隔片段。可以是暂停、静态帧或重复动画。不适用于包含内容的过场。', //intro
'致谢画面或片尾画面。不包含内容的结尾。', //outro
'展示此视频或同系列视频将出现的画面集锦,片段中所有内容都将在之后的正片中再次出现。', //preview
'仅用于音乐视频。此分类只能用于音乐视频中未包括于其他分类的部分。', //music_offtopic
'大部分人都在寻找的空降时间。类似于“封面在12:34”的评论。', //poi_highlight
"仅作为填充内容或增添趣味而添加的离题片段,这些内容对理解视频的主要内容并非必需。这不包括提供背景信息或上下文的片段。这是一个非常激进的分类,适用于当你不想看'娱乐性'内容的时候。", //filler
'仅用于对整个视频进行标记。适用于展示UP主免费或获得补贴后使用的产品、服务或场地的视频。', //exclusive_access
][index];
Color get color => [
Color(0xFF00d400), //sponsor
Color(0xFFffff00), //selfpromo
Color(0xFFcc00ff), //interaction
Color(0xFF00ffff), //intro
Color(0xFF0202ed), //outro
Color(0xFF008fd6), //preview
Color(0xFFff9900), //music_offtopic
Color(0xFFff1684), //poi_highlight
Color(0xFF7300FF), //filler
Color(0xFF008a5c), //exclusive_access
][index];
}
enum SkipType { alwaysSkip, skipOnce, skipManually, showOnly, disable }
extension SkipTypeExt on SkipType {
String get title => ['总是跳过', '跳过一次', '手动跳过', '仅显示', '禁用'][index];
}
class SegmentModel {
SegmentModel({
// ignore: non_constant_identifier_names
required this.UUID,
required this.segmentType,
required this.segment,
required this.skipType,
this.hasSkipped,
});
// ignore: non_constant_identifier_names
String UUID;
SegmentType segmentType;
Pair<int, int> segment;
SkipType skipType;
bool? hasSkipped;
}
class PostSegmentModel {
PostSegmentModel({
required this.segment,
required this.category,
required this.actionType,
});
Pair<int, int> segment;
SegmentType category;
ActionType actionType;
}
enum ActionType { skip, mute, full, poi }
extension ActionTypeExt on ActionType {
String get title => ['跳过', '静音', '整个视频', '精彩时刻'][index];
}
class VideoDetailController extends GetxController
with GetSingleTickerProviderStateMixin {
/// 路由传参

View File

@@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:math';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/video/detail/introduction/pay_coins_page.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:expandable/expandable.dart';
@@ -846,328 +846,3 @@ class VideoIntroController extends GetxController
return res;
}
}
class PayCoinsPage extends StatefulWidget {
const PayCoinsPage({
super.key,
required this.callback,
this.copyright = 1,
});
final Function callback;
final int copyright;
@override
State<PayCoinsPage> createState() => _PayCoinsPageState();
}
class _PayCoinsPageState extends State<PayCoinsPage>
with TickerProviderStateMixin {
bool _isPaying = false;
late final _controller = PageController(viewportFraction: 0.30);
int get _index => _controller.hasClients ? _controller.page?.round() ?? 0 : 0;
late AnimationController _slide22Controller;
late AnimationController _scale22Controller;
late AnimationController _coinSlideController;
late AnimationController _coinFadeController;
late AnimationController _boxAnimController;
final List _images = [
'assets/images/paycoins/ic_thunder_1.png',
'assets/images/paycoins/ic_thunder_2.png',
'assets/images/paycoins/ic_thunder_3.png',
];
late int _imageIndex = -1;
Timer? _timer;
bool get _showThunder => _imageIndex != -1 && _imageIndex != _images.length;
@override
void initState() {
super.initState();
_slide22Controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 50),
);
_scale22Controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 50),
);
_coinSlideController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
_coinFadeController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
);
_boxAnimController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 50),
);
_scale();
}
@override
void dispose() {
_timer?.cancel();
_slide22Controller.dispose();
_scale22Controller.dispose();
_coinSlideController.dispose();
_coinFadeController.dispose();
_boxAnimController.dispose();
_controller.dispose();
super.dispose();
}
void _scale() {
_scale22Controller.forward().whenComplete(() {
_scale22Controller.reverse();
});
}
void _onScroll(int index) {
_controller.animateToPage(
index,
duration: const Duration(milliseconds: 200),
curve: Curves.ease,
);
_scale();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return _buildBody(constraints.maxHeight > constraints.maxWidth);
});
}
Widget _buildBody(isV) => Stack(
alignment: Alignment.center,
children: [
Visibility(
visible: _showThunder,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: Image.asset(_images[_showThunder ? _imageIndex : 0]),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
children: [
Visibility(
visible: !_isPaying && widget.copyright == 1,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: GestureDetector(
onTap: _index == 0
? null
: () {
_onScroll(0);
},
child: Padding(
padding: const EdgeInsets.only(left: 12),
child: Image.asset(
width: 16,
height: 28,
_index == 0
? 'assets/images/paycoins/ic_left_disable.png'
: 'assets/images/paycoins/ic_left.png',
),
),
),
),
Expanded(
child: SizedBox(
height: 100,
child: PageView.builder(
itemCount: widget.copyright == 1 ? 2 : 1,
controller: _controller,
onPageChanged: (index) => setState(() {
_scale();
}),
itemBuilder: (context, index) {
return ListenableBuilder(
listenable: _controller,
builder: (context, child) {
double factor = index == 0 ? 1 : 0;
if (_controller.position.hasContentDimensions) {
factor = 1 - (_controller.page! - index).abs();
}
return Visibility(
visible: !_isPaying || _index == index,
child: Center(
child: SizedBox(
height: 70 + (factor * 30),
width: 70 + (factor * 30),
child: Stack(
alignment: Alignment.center,
children: [
SlideTransition(
position: _boxAnimController.drive(
Tween(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -0.2),
),
),
child: Image.asset(
'assets/images/paycoins/ic_pay_coins_box.png',
),
),
SlideTransition(
position: _coinSlideController.drive(
Tween(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -2),
),
),
child: FadeTransition(
opacity: Tween<double>(
begin: 1, end: 0)
.animate(_coinFadeController),
child: Image.asset(
height: 35 + (factor * 15),
width: 35 + (factor * 15),
index == 0
? 'assets/images/paycoins/ic_coins_one.png'
: 'assets/images/paycoins/ic_coins_two.png',
),
),
),
],
),
),
),
);
},
);
},
),
),
),
Visibility(
visible: !_isPaying && widget.copyright == 1,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: GestureDetector(
onTap: _index == 1
? null
: () {
_onScroll(1);
},
child: Padding(
padding: const EdgeInsets.only(right: 12),
child: Image.asset(
width: 16,
height: 28,
_index == 1
? 'assets/images/paycoins/ic_right_disable.png'
: 'assets/images/paycoins/ic_right.png',
),
),
),
),
],
),
const SizedBox(height: 25),
GestureDetector(
behavior: HitTestBehavior.opaque,
onPanUpdate: _handlePanUpdate,
child: SizedBox(
width: double.infinity,
height: 140,
child: Center(
child: GestureDetector(
onTap: _onPayCoin,
onPanUpdate: (e) => _handlePanUpdate(e, true),
child: ScaleTransition(
scale: _scale22Controller.drive(
Tween(begin: 1, end: 1.2),
),
child: SlideTransition(
position: _slide22Controller.drive(
Tween(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -0.2),
),
),
child: SizedBox(
width: 100,
height: 140,
child: Image.asset(
_index == 0
? 'assets/images/paycoins/ic_22_mario.png'
: 'assets/images/paycoins/ic_22_gun_sister.png',
),
),
),
),
),
),
),
),
SizedBox(
height:
(isV ? 50 : 0) + MediaQuery.of(context).padding.bottom),
],
),
],
);
void _handlePanUpdate(DragUpdateDetails e, [bool needV = false]) {
if (needV && e.delta.dy.abs() > max(2, e.delta.dx.abs())) {
if (e.delta.dy < 0) {
_onPayCoin();
}
} else if (widget.copyright == 1 &&
e.delta.dx.abs() > max(2, e.delta.dy.abs())) {
if (e.delta.dx > 0) {
if (_index == 1) {
_onScroll(0);
setState(() {});
}
} else {
if (_index == 0) {
_onScroll(1);
setState(() {});
}
}
}
}
void _onPayCoin() {
if (_isPaying) return;
setState(() {
_isPaying = true;
});
_slide22Controller.forward().whenComplete(() {
_slide22Controller.reverse().whenComplete(() {
if (_index == 1) {
_timer ??= Timer.periodic(const Duration(milliseconds: 50 ~/ 3), (_) {
if (_imageIndex != _images.length) {
setState(() {
_imageIndex = _imageIndex + 1;
});
} else {
_timer?.cancel();
}
});
}
_boxAnimController.forward().whenComplete(() {
_boxAnimController.reverse();
});
_coinSlideController.forward().whenComplete(() {
_coinFadeController.forward().whenComplete(() {
Get.back();
widget.callback(_index + 1);
});
});
});
});
}
}

View File

@@ -0,0 +1,330 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class PayCoinsPage extends StatefulWidget {
const PayCoinsPage({
super.key,
required this.callback,
this.copyright = 1,
});
final Function callback;
final int copyright;
@override
State<PayCoinsPage> createState() => _PayCoinsPageState();
}
class _PayCoinsPageState extends State<PayCoinsPage>
with TickerProviderStateMixin {
bool _isPaying = false;
late final _controller = PageController(viewportFraction: 0.30);
int get _index => _controller.hasClients ? _controller.page?.round() ?? 0 : 0;
late AnimationController _slide22Controller;
late AnimationController _scale22Controller;
late AnimationController _coinSlideController;
late AnimationController _coinFadeController;
late AnimationController _boxAnimController;
final List _images = [
'assets/images/paycoins/ic_thunder_1.png',
'assets/images/paycoins/ic_thunder_2.png',
'assets/images/paycoins/ic_thunder_3.png',
];
late int _imageIndex = -1;
Timer? _timer;
bool get _showThunder => _imageIndex != -1 && _imageIndex != _images.length;
@override
void initState() {
super.initState();
_slide22Controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 50),
);
_scale22Controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 50),
);
_coinSlideController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
_coinFadeController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
);
_boxAnimController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 50),
);
_scale();
}
@override
void dispose() {
_timer?.cancel();
_slide22Controller.dispose();
_scale22Controller.dispose();
_coinSlideController.dispose();
_coinFadeController.dispose();
_boxAnimController.dispose();
_controller.dispose();
super.dispose();
}
void _scale() {
_scale22Controller.forward().whenComplete(() {
_scale22Controller.reverse();
});
}
void _onScroll(int index) {
_controller.animateToPage(
index,
duration: const Duration(milliseconds: 200),
curve: Curves.ease,
);
_scale();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return _buildBody(constraints.maxHeight > constraints.maxWidth);
});
}
Widget _buildBody(isV) => Stack(
alignment: Alignment.center,
children: [
Visibility(
visible: _showThunder,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: Image.asset(_images[_showThunder ? _imageIndex : 0]),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
children: [
Visibility(
visible: !_isPaying && widget.copyright == 1,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: GestureDetector(
onTap: _index == 0
? null
: () {
_onScroll(0);
},
child: Padding(
padding: const EdgeInsets.only(left: 12),
child: Image.asset(
width: 16,
height: 28,
_index == 0
? 'assets/images/paycoins/ic_left_disable.png'
: 'assets/images/paycoins/ic_left.png',
),
),
),
),
Expanded(
child: SizedBox(
height: 100,
child: PageView.builder(
itemCount: widget.copyright == 1 ? 2 : 1,
controller: _controller,
onPageChanged: (index) => setState(() {
_scale();
}),
itemBuilder: (context, index) {
return ListenableBuilder(
listenable: _controller,
builder: (context, child) {
double factor = index == 0 ? 1 : 0;
if (_controller.position.hasContentDimensions) {
factor = 1 - (_controller.page! - index).abs();
}
return Visibility(
visible: !_isPaying || _index == index,
child: Center(
child: SizedBox(
height: 70 + (factor * 30),
width: 70 + (factor * 30),
child: Stack(
alignment: Alignment.center,
children: [
SlideTransition(
position: _boxAnimController.drive(
Tween(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -0.2),
),
),
child: Image.asset(
'assets/images/paycoins/ic_pay_coins_box.png',
),
),
SlideTransition(
position: _coinSlideController.drive(
Tween(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -2),
),
),
child: FadeTransition(
opacity: Tween<double>(
begin: 1, end: 0)
.animate(_coinFadeController),
child: Image.asset(
height: 35 + (factor * 15),
width: 35 + (factor * 15),
index == 0
? 'assets/images/paycoins/ic_coins_one.png'
: 'assets/images/paycoins/ic_coins_two.png',
),
),
),
],
),
),
),
);
},
);
},
),
),
),
Visibility(
visible: !_isPaying && widget.copyright == 1,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: GestureDetector(
onTap: _index == 1
? null
: () {
_onScroll(1);
},
child: Padding(
padding: const EdgeInsets.only(right: 12),
child: Image.asset(
width: 16,
height: 28,
_index == 1
? 'assets/images/paycoins/ic_right_disable.png'
: 'assets/images/paycoins/ic_right.png',
),
),
),
),
],
),
const SizedBox(height: 25),
GestureDetector(
behavior: HitTestBehavior.opaque,
onPanUpdate: _handlePanUpdate,
child: SizedBox(
width: double.infinity,
height: 140,
child: Center(
child: GestureDetector(
onTap: _onPayCoin,
onPanUpdate: (e) => _handlePanUpdate(e, true),
child: ScaleTransition(
scale: _scale22Controller.drive(
Tween(begin: 1, end: 1.2),
),
child: SlideTransition(
position: _slide22Controller.drive(
Tween(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -0.2),
),
),
child: SizedBox(
width: 100,
height: 140,
child: Image.asset(
_index == 0
? 'assets/images/paycoins/ic_22_mario.png'
: 'assets/images/paycoins/ic_22_gun_sister.png',
),
),
),
),
),
),
),
),
SizedBox(
height:
(isV ? 50 : 0) + MediaQuery.of(context).padding.bottom),
],
),
],
);
void _handlePanUpdate(DragUpdateDetails e, [bool needV = false]) {
if (needV && e.delta.dy.abs() > max(2, e.delta.dx.abs())) {
if (e.delta.dy < 0) {
_onPayCoin();
}
} else if (widget.copyright == 1 &&
e.delta.dx.abs() > max(2, e.delta.dy.abs())) {
if (e.delta.dx > 0) {
if (_index == 1) {
_onScroll(0);
setState(() {});
}
} else {
if (_index == 0) {
_onScroll(1);
setState(() {});
}
}
}
}
void _onPayCoin() {
if (_isPaying) return;
setState(() {
_isPaying = true;
});
_slide22Controller.forward().whenComplete(() {
_slide22Controller.reverse().whenComplete(() {
if (_index == 1) {
_timer ??= Timer.periodic(const Duration(milliseconds: 50 ~/ 3), (_) {
if (_imageIndex != _images.length) {
setState(() {
_imageIndex = _imageIndex + 1;
});
} else {
_timer?.cancel();
}
});
}
_boxAnimController.forward().whenComplete(() {
_boxAnimController.reverse();
});
_coinSlideController.forward().whenComplete(() {
_coinFadeController.forward().whenComplete(() {
Get.back();
widget.callback(_index + 1);
});
});
});
});
}
}

View File

@@ -8,6 +8,7 @@ import 'package:PiliPlus/models/video/reply/emote.dart';
import 'package:PiliPlus/models/video/reply/item.dart';
import 'package:PiliPlus/pages/emote/index.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
import 'toolbar_icon_button.dart';
@@ -38,7 +39,8 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
final GlobalKey _formKey = GlobalKey<FormState>();
late double emoteHeight = 0.0;
double keyboardHeight = 0.0; // 键盘高度
final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间
final _debouncer =
Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间
String toolbarType = 'input';
bool _enablePublish = false;
final _publishStream = StreamController<bool>();
@@ -121,7 +123,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
// 键盘高度
final viewInsets = EdgeInsets.fromViewPadding(
View.of(context).viewInsets, View.of(context).devicePixelRatio);
_debouncer.run(() {
_debouncer(() {
if (!mounted) return;
if (keyboardHeight == 0 && emoteHeight == 0) {
emoteHeight = keyboardHeight =
@@ -275,22 +277,3 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
);
}
}
typedef DebounceCallback = void Function();
class Debouncer {
DebounceCallback? callback;
final int? milliseconds;
Timer? _timer;
Debouncer({this.milliseconds});
run(DebounceCallback callback) {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds!), () {
callback();
});
}
}

View File

@@ -3,8 +3,7 @@ import 'dart:math';
import 'package:PiliPlus/common/widgets/icon_button.dart';
import 'package:PiliPlus/http/danmaku.dart';
import 'package:PiliPlus/pages/setting/sponsor_block_page.dart'
show SlideColorPicker;
import 'package:PiliPlus/pages/setting/slide_color_picker.dart';
import 'package:PiliPlus/pages/video/detail/reply_new/reply_page.dart'
show PanelType;
import 'package:PiliPlus/utils/extension.dart';