mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: custom horizontal preview
Closes #117 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -8,6 +8,7 @@ import 'package:flutter_html/flutter_html.dart';
|
|||||||
Widget articleContent({
|
Widget articleContent({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required List<ArticleContentModel> list,
|
required List<ArticleContentModel> list,
|
||||||
|
Function(List<String>, int)? callback,
|
||||||
}) {
|
}) {
|
||||||
List<String>? imgList = list
|
List<String>? imgList = list
|
||||||
.where((item) => item.pic != null)
|
.where((item) => item.pic != null)
|
||||||
@@ -59,10 +60,17 @@ Widget articleContent({
|
|||||||
tag: item.pic!.pics!.first.url!,
|
tag: item.pic!.pics!.first.url!,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (callback != null) {
|
||||||
|
callback(
|
||||||
|
imgList,
|
||||||
|
imgList.indexOf(item.pic!.pics!.first.url!),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
context.imageView(
|
context.imageView(
|
||||||
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
|
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
|
||||||
imgList: imgList,
|
imgList: imgList,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
width: constraints.maxWidth,
|
width: constraints.maxWidth,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Widget htmlRender({
|
|||||||
int? imgCount,
|
int? imgCount,
|
||||||
List<String>? imgList,
|
List<String>? imgList,
|
||||||
required double constrainedWidth,
|
required double constrainedWidth,
|
||||||
|
Function(List<String>, int)? callback,
|
||||||
}) {
|
}) {
|
||||||
return SelectionArea(
|
return SelectionArea(
|
||||||
child: Html(
|
child: Html(
|
||||||
@@ -49,9 +50,13 @@ Widget htmlRender({
|
|||||||
tag: imgUrl,
|
tag: imgUrl,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (callback != null) {
|
||||||
|
callback([imgUrl], 0);
|
||||||
|
} else {
|
||||||
context.imageView(
|
context.imageView(
|
||||||
imgList: [imgUrl],
|
imgList: [imgUrl],
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
width: isEmote ? 22 : constrainedWidth,
|
width: isEmote ? 22 : constrainedWidth,
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ class ImageModel {
|
|||||||
|
|
||||||
Widget imageview(
|
Widget imageview(
|
||||||
double maxWidth,
|
double maxWidth,
|
||||||
List<ImageModel> picArr, [
|
List<ImageModel> picArr, {
|
||||||
VoidCallback? onViewImage,
|
VoidCallback? onViewImage,
|
||||||
ValueChanged<int>? onDismissed,
|
ValueChanged<int>? onDismissed,
|
||||||
]) {
|
Function(List<String>, int)? callback,
|
||||||
|
}) {
|
||||||
double imageWidth = (maxWidth - 2 * 5) / 3;
|
double imageWidth = (maxWidth - 2 * 5) / 3;
|
||||||
double imageHeight = imageWidth;
|
double imageHeight = imageWidth;
|
||||||
if (picArr.length == 1) {
|
if (picArr.length == 1) {
|
||||||
@@ -59,12 +60,16 @@ Widget imageview(
|
|||||||
tag: picArr[index].url,
|
tag: picArr[index].url,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (callback != null) {
|
||||||
|
callback(picArr.map((item) => item.url).toList(), index);
|
||||||
|
} else {
|
||||||
onViewImage?.call();
|
onViewImage?.call();
|
||||||
context.imageView(
|
context.imageView(
|
||||||
initialPage: index,
|
initialPage: index,
|
||||||
imgList: picArr.map((item) => item.url).toList(),
|
imgList: picArr.map((item) => item.url).toList(),
|
||||||
onDismissed: onDismissed,
|
onDismissed: onDismissed,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
|||||||
@@ -42,8 +42,11 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
|
|||||||
this.minScale = 1.0,
|
this.minScale = 1.0,
|
||||||
this.onPageChanged,
|
this.onPageChanged,
|
||||||
this.onDismissed,
|
this.onDismissed,
|
||||||
|
this.setStatusBar,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final bool? setStatusBar;
|
||||||
|
|
||||||
/// The sources to show.
|
/// The sources to show.
|
||||||
final List<String> sources;
|
final List<String> sources;
|
||||||
|
|
||||||
@@ -108,8 +111,10 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
});
|
});
|
||||||
|
|
||||||
currentIndex = widget.initIndex;
|
currentIndex = widget.initIndex;
|
||||||
|
if (widget.setStatusBar != false) {
|
||||||
setStatusBar();
|
setStatusBar();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setStatusBar() async {
|
setStatusBar() async {
|
||||||
if (Platform.isIOS || Platform.isAndroid) {
|
if (Platform.isIOS || Platform.isAndroid) {
|
||||||
@@ -125,9 +130,11 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
_pageController?.dispose();
|
_pageController?.dispose();
|
||||||
_animationController.removeListener(() {});
|
_animationController.removeListener(() {});
|
||||||
_animationController.dispose();
|
_animationController.dispose();
|
||||||
|
if (widget.setStatusBar != false) {
|
||||||
if (Platform.isIOS || Platform.isAndroid) {
|
if (Platform.isIOS || Platform.isAndroid) {
|
||||||
StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE);
|
StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (int index = 0; index < widget.sources.length; index++) {
|
for (int index = 0; index < widget.sources.length; index++) {
|
||||||
CachedNetworkImageProvider(_getActualUrl(index)).evict();
|
CachedNetworkImageProvider(_getActualUrl(index)).evict();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/pages/common/reply_controller.dart';
|
import 'package:PiliPlus/pages/common/reply_controller.dart';
|
||||||
import 'package:PiliPlus/utils/global_data.dart';
|
import 'package:PiliPlus/utils/global_data.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPlus/http/html.dart';
|
import 'package:PiliPlus/http/html.dart';
|
||||||
import 'package:PiliPlus/http/reply.dart';
|
import 'package:PiliPlus/http/reply.dart';
|
||||||
@@ -14,6 +15,8 @@ class DynamicDetailController extends ReplyController {
|
|||||||
dynamic item;
|
dynamic item;
|
||||||
int? floor;
|
int? floor;
|
||||||
|
|
||||||
|
late final horizontalPreview = GStorage.horizontalPreview;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:math';
|
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/interactiveviewer_gallery/interactiveviewer_gallery.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/pages/video/detail/reply/widgets/reply_item.dart';
|
import 'package:PiliPlus/pages/video/detail/reply/widgets/reply_item.dart';
|
||||||
@@ -51,6 +52,29 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
|
|
||||||
late final List<double> _ratio = GStorage.dynamicDetailRatio;
|
late final List<double> _ratio = GStorage.dynamicDetailRatio;
|
||||||
|
|
||||||
|
bool get _horizontalPreview =>
|
||||||
|
context.orientation == Orientation.landscape &&
|
||||||
|
_dynamicDetailController.horizontalPreview;
|
||||||
|
|
||||||
|
late final _key = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
get _getImageCallback => _horizontalPreview
|
||||||
|
? (imgList, index) {
|
||||||
|
_key.currentState?.showBottomSheet(
|
||||||
|
(context) {
|
||||||
|
return InteractiveviewerGallery(
|
||||||
|
sources: imgList,
|
||||||
|
initIndex: index,
|
||||||
|
setStatusBar: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableDrag: false,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -299,6 +323,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
child: DynamicPanel(
|
child: DynamicPanel(
|
||||||
item: _dynamicDetailController.item,
|
item: _dynamicDetailController.item,
|
||||||
source: 'detail',
|
source: 'detail',
|
||||||
|
callback: _getImageCallback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
replyPersistentHeader(context),
|
replyPersistentHeader(context),
|
||||||
@@ -326,6 +351,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
child: DynamicPanel(
|
child: DynamicPanel(
|
||||||
item: _dynamicDetailController.item,
|
item: _dynamicDetailController.item,
|
||||||
source: 'detail',
|
source: 'detail',
|
||||||
|
callback: _getImageCallback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -335,6 +361,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: _ratio[1].toInt(),
|
flex: _ratio[1].toInt(),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
key: _key,
|
||||||
body: refreshIndicator(
|
body: refreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await _dynamicDetailController.onRefresh();
|
await _dynamicDetailController.onRefresh();
|
||||||
@@ -499,6 +526,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
isTop:
|
isTop:
|
||||||
_dynamicDetailController.hasUpTop && index == 0,
|
_dynamicDetailController.hasUpTop && index == 0,
|
||||||
upMid: loadingState.response.subjectControl.upMid,
|
upMid: loadingState.response.subjectControl.upMid,
|
||||||
|
callback: _getImageCallback,
|
||||||
)
|
)
|
||||||
: ReplyItem(
|
: ReplyItem(
|
||||||
replyItem: loadingState.response.replies[index],
|
replyItem: loadingState.response.replies[index],
|
||||||
@@ -515,6 +543,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
onDelete: _dynamicDetailController.onMDelete,
|
onDelete: _dynamicDetailController.onMDelete,
|
||||||
|
callback: _getImageCallback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:PiliPlus/utils/utils.dart';
|
|||||||
import '../../../common/constants.dart';
|
import '../../../common/constants.dart';
|
||||||
import 'pic_panel.dart';
|
import 'pic_panel.dart';
|
||||||
|
|
||||||
Widget articlePanel(item, context, {floor = 1}) {
|
Widget articlePanel(item, context, callback, {floor = 1}) {
|
||||||
TextStyle authorStyle =
|
TextStyle authorStyle =
|
||||||
TextStyle(color: Theme.of(context).colorScheme.primary);
|
TextStyle(color: Theme.of(context).colorScheme.primary);
|
||||||
return Padding(
|
return Padding(
|
||||||
@@ -52,7 +52,7 @@ Widget articlePanel(item, context, {floor = 1}) {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
],
|
],
|
||||||
picWidget(item, context)
|
picWidget(item, context, callback)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,15 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'rich_node_panel.dart';
|
import 'rich_node_panel.dart';
|
||||||
|
|
||||||
class Content extends StatelessWidget {
|
Widget content(context, item, source, callback) {
|
||||||
final dynamic item;
|
|
||||||
final String? source;
|
|
||||||
const Content({
|
|
||||||
super.key,
|
|
||||||
this.item,
|
|
||||||
this.source,
|
|
||||||
});
|
|
||||||
|
|
||||||
InlineSpan picsNodes() {
|
InlineSpan picsNodes() {
|
||||||
return WidgetSpan(
|
return WidgetSpan(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
@@ -27,13 +19,12 @@ class Content extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
callback: callback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
TextStyle authorStyle =
|
TextStyle authorStyle =
|
||||||
TextStyle(color: Theme.of(context).colorScheme.primary);
|
TextStyle(color: Theme.of(context).colorScheme.primary);
|
||||||
InlineSpan? richNodes = richNode(item, context);
|
InlineSpan? richNodes = richNode(item, context);
|
||||||
@@ -83,4 +74,3 @@ class Content extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ class DynamicPanel extends StatelessWidget {
|
|||||||
final dynamic item;
|
final dynamic item;
|
||||||
final String? source;
|
final String? source;
|
||||||
final Function? onRemove;
|
final Function? onRemove;
|
||||||
|
final Function(List<String>, int)? callback;
|
||||||
|
|
||||||
DynamicPanel({
|
DynamicPanel({
|
||||||
required this.item,
|
required this.item,
|
||||||
this.source,
|
this.source,
|
||||||
this.onRemove,
|
this.onRemove,
|
||||||
|
this.callback,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,8 +60,8 @@ class DynamicPanel extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (item!.modules!.moduleDynamic!.desc != null ||
|
if (item!.modules!.moduleDynamic!.desc != null ||
|
||||||
item!.modules!.moduleDynamic!.major != null)
|
item!.modules!.moduleDynamic!.major != null)
|
||||||
Content(item: item, source: source),
|
content(context, item, source, callback),
|
||||||
forWard(item, context, _dynamicsController, source),
|
forWard(item, context, _dynamicsController, source, callback),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
if (source == null) ActionPanel(item: item),
|
if (source == null) ActionPanel(item: item),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import 'pic_panel.dart';
|
|||||||
import 'rich_node_panel.dart';
|
import 'rich_node_panel.dart';
|
||||||
import 'video_panel.dart';
|
import 'video_panel.dart';
|
||||||
|
|
||||||
InlineSpan picsNodes(List<OpusPicsModel> pics) {
|
InlineSpan picsNodes(List<OpusPicsModel> pics, callback) {
|
||||||
return WidgetSpan(
|
return WidgetSpan(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) => imageview(
|
builder: (context, constraints) => imageview(
|
||||||
@@ -29,12 +29,13 @@ InlineSpan picsNodes(List<OpusPicsModel> pics) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
callback: callback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget forWard(item, context, ctr, source, {floor = 1}) {
|
Widget forWard(item, context, ctr, source, callback, {floor = 1}) {
|
||||||
TextStyle authorStyle =
|
TextStyle authorStyle =
|
||||||
TextStyle(color: Theme.of(context).colorScheme.primary);
|
TextStyle(color: Theme.of(context).colorScheme.primary);
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
|
|||||||
),
|
),
|
||||||
if (hasPics) ...[
|
if (hasPics) ...[
|
||||||
Text.rich(
|
Text.rich(
|
||||||
picsNodes(pics),
|
picsNodes(pics, callback),
|
||||||
// semanticsLabel: '动态图片',
|
// semanticsLabel: '动态图片',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -110,7 +111,7 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
|
|||||||
padding: floor == 2
|
padding: floor == 2
|
||||||
? EdgeInsets.zero
|
? EdgeInsets.zero
|
||||||
: const EdgeInsets.only(left: 12, right: 12),
|
: const EdgeInsets.only(left: 12, right: 12),
|
||||||
child: picWidget(item, context),
|
child: picWidget(item, context, callback),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// 附加内容 商品信息、直播预约等等
|
/// 附加内容 商品信息、直播预约等等
|
||||||
@@ -129,7 +130,7 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
|
|||||||
// 文章
|
// 文章
|
||||||
case 'DYNAMIC_TYPE_ARTICLE':
|
case 'DYNAMIC_TYPE_ARTICLE':
|
||||||
return item is ItemOrigModel
|
return item is ItemOrigModel
|
||||||
? articlePanel(item, context, floor: floor)
|
? articlePanel(item, context, callback, floor: floor)
|
||||||
: const SizedBox.shrink();
|
: const SizedBox.shrink();
|
||||||
// return Container(
|
// return Container(
|
||||||
// padding:
|
// padding:
|
||||||
@@ -144,7 +145,8 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
|
|||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 8),
|
const EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 8),
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
||||||
child: forWard(item.orig, context, ctr, source, floor: floor + 1),
|
child: forWard(item.orig, context, ctr, source, callback,
|
||||||
|
floor: floor + 1),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
// 直播
|
// 直播
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:PiliPlus/common/widgets/imageview.dart';
|
import 'package:PiliPlus/common/widgets/imageview.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
Widget picWidget(item, context) {
|
Widget picWidget(item, context, callback) {
|
||||||
String type = item.modules.moduleDynamic.major.type;
|
String type = item.modules.moduleDynamic.major.type;
|
||||||
if (type == 'MAJOR_TYPE_OPUS') {
|
if (type == 'MAJOR_TYPE_OPUS') {
|
||||||
/// fix 图片跟rich_node_panel重复
|
/// fix 图片跟rich_node_panel重复
|
||||||
@@ -20,6 +20,7 @@ Widget picWidget(item, context) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
callback: callback,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/pages/common/reply_controller.dart';
|
import 'package:PiliPlus/pages/common/reply_controller.dart';
|
||||||
import 'package:PiliPlus/utils/global_data.dart';
|
import 'package:PiliPlus/utils/global_data.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPlus/http/html.dart';
|
import 'package:PiliPlus/http/html.dart';
|
||||||
import 'package:PiliPlus/http/reply.dart';
|
import 'package:PiliPlus/http/reply.dart';
|
||||||
@@ -17,6 +18,8 @@ class HtmlRenderController extends ReplyController {
|
|||||||
|
|
||||||
RxBool loaded = false.obs;
|
RxBool loaded = false.obs;
|
||||||
|
|
||||||
|
late final horizontalPreview = GStorage.horizontalPreview;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:PiliPlus/common/widgets/article_content.dart';
|
import 'package:PiliPlus/common/widgets/article_content.dart';
|
||||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/pages/video/detail/reply/widgets/reply_item.dart';
|
import 'package:PiliPlus/pages/video/detail/reply/widgets/reply_item.dart';
|
||||||
import 'package:PiliPlus/pages/video/detail/reply/widgets/reply_item_grpc.dart';
|
import 'package:PiliPlus/pages/video/detail/reply/widgets/reply_item_grpc.dart';
|
||||||
@@ -44,6 +45,29 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
|
|
||||||
late final List<double> _ratio = GStorage.dynamicDetailRatio;
|
late final List<double> _ratio = GStorage.dynamicDetailRatio;
|
||||||
|
|
||||||
|
bool get _horizontalPreview =>
|
||||||
|
context.orientation == Orientation.landscape &&
|
||||||
|
_htmlRenderCtr.horizontalPreview;
|
||||||
|
|
||||||
|
late final _key = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
get _getImageCallback => _horizontalPreview
|
||||||
|
? (imgList, index) {
|
||||||
|
_key.currentState?.showBottomSheet(
|
||||||
|
(context) {
|
||||||
|
return InteractiveviewerGallery(
|
||||||
|
sources: imgList,
|
||||||
|
initIndex: index,
|
||||||
|
setStatusBar: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableDrag: false,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -336,6 +360,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: _ratio[1].toInt(),
|
flex: _ratio[1].toInt(),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
key: _key,
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
controller: _htmlRenderCtr.scrollController,
|
controller: _htmlRenderCtr.scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
@@ -442,6 +467,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
onDelete: _htmlRenderCtr.onMDelete,
|
onDelete: _htmlRenderCtr.onMDelete,
|
||||||
isTop: _htmlRenderCtr.hasUpTop && index == 0,
|
isTop: _htmlRenderCtr.hasUpTop && index == 0,
|
||||||
upMid: loadingState.response.subjectControl.upMid,
|
upMid: loadingState.response.subjectControl.upMid,
|
||||||
|
callback: _getImageCallback,
|
||||||
)
|
)
|
||||||
: ReplyItem(
|
: ReplyItem(
|
||||||
replyItem: loadingState.response.replies[index],
|
replyItem: loadingState.response.replies[index],
|
||||||
@@ -458,6 +484,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
onDelete: _htmlRenderCtr.onMDelete,
|
onDelete: _htmlRenderCtr.onMDelete,
|
||||||
|
callback: _getImageCallback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -534,11 +561,12 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(_htmlRenderCtr.response['uname'],
|
Text(
|
||||||
|
_htmlRenderCtr.response['uname'],
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize:
|
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
),
|
||||||
)),
|
),
|
||||||
Text(
|
Text(
|
||||||
_htmlRenderCtr.response['updateTime'],
|
_htmlRenderCtr.response['updateTime'],
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -561,6 +589,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
? articleContent(
|
? articleContent(
|
||||||
context: context,
|
context: context,
|
||||||
list: _htmlRenderCtr.response['content'],
|
list: _htmlRenderCtr.response['content'],
|
||||||
|
callback: _getImageCallback,
|
||||||
)
|
)
|
||||||
: SliverToBoxAdapter(
|
: SliverToBoxAdapter(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
@@ -568,6 +597,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
context: context,
|
context: context,
|
||||||
htmlContent: _htmlRenderCtr.response['content'],
|
htmlContent: _htmlRenderCtr.response['content'],
|
||||||
constrainedWidth: constraints.maxWidth,
|
constrainedWidth: constraints.maxWidth,
|
||||||
|
callback: _getImageCallback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1655,6 +1655,13 @@ List<SettingsModel> get extraSettings => [
|
|||||||
setKey: SettingBoxKey.continuePlayingPart,
|
setKey: SettingBoxKey.continuePlayingPart,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
|
SettingsModel(
|
||||||
|
settingsType: SettingsType.sw1tch,
|
||||||
|
title: '横屏在侧栏打开图片预览',
|
||||||
|
leading: Icon(Icons.photo_outlined),
|
||||||
|
setKey: SettingBoxKey.horizontalPreview,
|
||||||
|
defaultVal: false,
|
||||||
|
),
|
||||||
SettingsModel(
|
SettingsModel(
|
||||||
settingsType: SettingsType.sw1tch,
|
settingsType: SettingsType.sw1tch,
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
|
|||||||
@@ -246,6 +246,8 @@ class VideoDetailController extends GetxController
|
|||||||
imageStatus = false;
|
imageStatus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
late final horizontalPreview = GStorage.horizontalPreview;
|
||||||
|
|
||||||
// 页面来源 稍后再看 收藏夹
|
// 页面来源 稍后再看 收藏夹
|
||||||
String sourceType = 'normal';
|
String sourceType = 'normal';
|
||||||
late bool _mediaDesc = false;
|
late bool _mediaDesc = false;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class VideoReplyPanel extends StatefulWidget {
|
|||||||
final Function replyReply;
|
final Function replyReply;
|
||||||
final VoidCallback? onViewImage;
|
final VoidCallback? onViewImage;
|
||||||
final ValueChanged<int>? onDismissed;
|
final ValueChanged<int>? onDismissed;
|
||||||
|
final Function(List<String>, int)? callback;
|
||||||
|
|
||||||
const VideoReplyPanel({
|
const VideoReplyPanel({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -35,6 +36,7 @@ class VideoReplyPanel extends StatefulWidget {
|
|||||||
required this.replyReply,
|
required this.replyReply,
|
||||||
this.onViewImage,
|
this.onViewImage,
|
||||||
this.onDismissed,
|
this.onDismissed,
|
||||||
|
this.callback,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -264,6 +266,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
getTag: () => heroTag,
|
getTag: () => heroTag,
|
||||||
onViewImage: widget.onViewImage,
|
onViewImage: widget.onViewImage,
|
||||||
onDismissed: widget.onDismissed,
|
onDismissed: widget.onDismissed,
|
||||||
|
callback: widget.callback,
|
||||||
)
|
)
|
||||||
: ReplyItem(
|
: ReplyItem(
|
||||||
replyItem: loadingState.response.replies[index],
|
replyItem: loadingState.response.replies[index],
|
||||||
@@ -282,6 +285,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
onViewImage: widget.onViewImage,
|
onViewImage: widget.onViewImage,
|
||||||
onDismissed: widget.onDismissed,
|
onDismissed: widget.onDismissed,
|
||||||
getTag: () => heroTag,
|
getTag: () => heroTag,
|
||||||
|
callback: widget.callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
this.onViewImage,
|
this.onViewImage,
|
||||||
this.onDismissed,
|
this.onDismissed,
|
||||||
this.getTag,
|
this.getTag,
|
||||||
|
this.callback,
|
||||||
});
|
});
|
||||||
final ReplyItemModel? replyItem;
|
final ReplyItemModel? replyItem;
|
||||||
final String? replyLevel;
|
final String? replyLevel;
|
||||||
@@ -47,6 +48,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
final VoidCallback? onViewImage;
|
final VoidCallback? onViewImage;
|
||||||
final ValueChanged<int>? onDismissed;
|
final ValueChanged<int>? onDismissed;
|
||||||
final Function? getTag;
|
final Function? getTag;
|
||||||
|
final Function(List<String>, int)? callback;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -975,8 +977,9 @@ class ReplyItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
onViewImage,
|
onViewImage: onViewImage,
|
||||||
onDismissed,
|
onDismissed: onDismissed,
|
||||||
|
callback: callback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
|||||||
this.getTag,
|
this.getTag,
|
||||||
this.onViewImage,
|
this.onViewImage,
|
||||||
this.onDismissed,
|
this.onDismissed,
|
||||||
|
this.callback,
|
||||||
});
|
});
|
||||||
final ReplyInfo replyItem;
|
final ReplyInfo replyItem;
|
||||||
final String? replyLevel;
|
final String? replyLevel;
|
||||||
@@ -53,6 +54,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
|||||||
final Function? getTag;
|
final Function? getTag;
|
||||||
final VoidCallback? onViewImage;
|
final VoidCallback? onViewImage;
|
||||||
final ValueChanged<int>? onDismissed;
|
final ValueChanged<int>? onDismissed;
|
||||||
|
final Function(List<String>, int)? callback;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -1006,8 +1008,9 @@ class ReplyItemGrpc extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
onViewImage,
|
onViewImage: onViewImage,
|
||||||
onDismissed,
|
onDismissed: onDismissed,
|
||||||
|
callback: callback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
|||||||
import 'package:PiliPlus/models/video/reply/item.dart';
|
import 'package:PiliPlus/models/video/reply/item.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||||
import 'package:PiliPlus/utils/global_data.dart';
|
import 'package:PiliPlus/utils/global_data.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:PiliPlus/http/reply.dart';
|
import 'package:PiliPlus/http/reply.dart';
|
||||||
@@ -37,11 +38,13 @@ class VideoReplyReplyController extends CommonController
|
|||||||
RxInt count = (-1).obs;
|
RxInt count = (-1).obs;
|
||||||
int? upMid;
|
int? upMid;
|
||||||
|
|
||||||
|
dynamic firstFloor;
|
||||||
|
|
||||||
int? index;
|
int? index;
|
||||||
AnimationController? controller;
|
AnimationController? controller;
|
||||||
Animation<Color?>? colorAnimation;
|
Animation<Color?>? colorAnimation;
|
||||||
|
|
||||||
dynamic firstFloor;
|
late final horizontalPreview = GStorage.horizontalPreview;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart';
|
||||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
@@ -58,6 +59,10 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
dynamic get firstFloor =>
|
dynamic get firstFloor =>
|
||||||
widget.firstFloor ?? _videoReplyReplyController.firstFloor;
|
widget.firstFloor ?? _videoReplyReplyController.firstFloor;
|
||||||
|
|
||||||
|
bool get _horizontalPreview =>
|
||||||
|
context.orientation == Orientation.landscape &&
|
||||||
|
_videoReplyReplyController.horizontalPreview;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -174,6 +179,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
isTop: widget.isTop,
|
isTop: widget.isTop,
|
||||||
onViewImage: widget.onViewImage,
|
onViewImage: widget.onViewImage,
|
||||||
onDismissed: widget.onDismissed,
|
onDismissed: widget.onDismissed,
|
||||||
|
callback: _getImageCallback,
|
||||||
)
|
)
|
||||||
: ReplyItem(
|
: ReplyItem(
|
||||||
replyItem: firstFloor,
|
replyItem: firstFloor,
|
||||||
@@ -186,6 +192,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
},
|
},
|
||||||
onViewImage: widget.onViewImage,
|
onViewImage: widget.onViewImage,
|
||||||
onDismissed: widget.onDismissed,
|
onDismissed: widget.onDismissed,
|
||||||
|
callback: _getImageCallback,
|
||||||
);
|
);
|
||||||
} else if (index == 1) {
|
} else if (index == 1) {
|
||||||
return Divider(
|
return Divider(
|
||||||
@@ -271,6 +278,23 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
get _getImageCallback => _horizontalPreview
|
||||||
|
? (imgList, index) {
|
||||||
|
_key.currentState?.showBottomSheet(
|
||||||
|
(context) {
|
||||||
|
return InteractiveviewerGallery(
|
||||||
|
sources: imgList,
|
||||||
|
initIndex: index,
|
||||||
|
setStatusBar: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableDrag: false,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
void _onReply(dynamic item, int index) {
|
void _onReply(dynamic item, int index) {
|
||||||
dynamic oid = item?.oid.toInt();
|
dynamic oid = item?.oid.toInt();
|
||||||
dynamic root = GlobalData().grpcReply ? item?.id.toInt() : item?.rpid;
|
dynamic root = GlobalData().grpcReply ? item?.id.toInt() : item?.rpid;
|
||||||
@@ -457,6 +481,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
},
|
},
|
||||||
onViewImage: widget.onViewImage,
|
onViewImage: widget.onViewImage,
|
||||||
onDismissed: widget.onDismissed,
|
onDismissed: widget.onDismissed,
|
||||||
|
callback: _getImageCallback,
|
||||||
)
|
)
|
||||||
: ReplyItem(
|
: ReplyItem(
|
||||||
replyItem: replyItem,
|
replyItem: replyItem,
|
||||||
@@ -477,6 +502,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
},
|
},
|
||||||
onViewImage: widget.onViewImage,
|
onViewImage: widget.onViewImage,
|
||||||
onDismissed: widget.onDismissed,
|
onDismissed: widget.onDismissed,
|
||||||
|
callback: _getImageCallback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.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/list_sheet.dart';
|
import 'package:PiliPlus/common/widgets/list_sheet.dart';
|
||||||
import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
|
import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
@@ -94,6 +95,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
context.orientation == Orientation.landscape &&
|
context.orientation == Orientation.landscape &&
|
||||||
videoDetailController.horizontalSeasonPanel;
|
videoDetailController.horizontalSeasonPanel;
|
||||||
|
|
||||||
|
bool get _horizontalPreview =>
|
||||||
|
context.orientation == Orientation.landscape &&
|
||||||
|
videoDetailController.horizontalPreview;
|
||||||
|
|
||||||
StreamSubscription? _listenerDetail;
|
StreamSubscription? _listenerDetail;
|
||||||
StreamSubscription? _listenerLoadingState;
|
StreamSubscription? _listenerLoadingState;
|
||||||
StreamSubscription? _listenerCid;
|
StreamSubscription? _listenerCid;
|
||||||
@@ -1543,6 +1548,22 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
replyReply: replyReply,
|
replyReply: replyReply,
|
||||||
onViewImage: videoDetailController.onViewImage,
|
onViewImage: videoDetailController.onViewImage,
|
||||||
onDismissed: videoDetailController.onDismissed,
|
onDismissed: videoDetailController.onDismissed,
|
||||||
|
callback: _horizontalPreview
|
||||||
|
? (imgList, index) {
|
||||||
|
videoDetailController.childKey.currentState?.showBottomSheet(
|
||||||
|
(context) {
|
||||||
|
return InteractiveviewerGallery(
|
||||||
|
sources: imgList,
|
||||||
|
initIndex: index,
|
||||||
|
setStatusBar: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableDrag: false,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -304,6 +304,9 @@ class GStorage {
|
|||||||
static bool get autoUpdate =>
|
static bool get autoUpdate =>
|
||||||
GStorage.setting.get(SettingBoxKey.autoUpdate, defaultValue: true);
|
GStorage.setting.get(SettingBoxKey.autoUpdate, defaultValue: true);
|
||||||
|
|
||||||
|
static bool get horizontalPreview => GStorage.setting
|
||||||
|
.get(SettingBoxKey.horizontalPreview, defaultValue: false);
|
||||||
|
|
||||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||||
|
|
||||||
@@ -513,6 +516,7 @@ class SettingBoxKey {
|
|||||||
badCertificateCallback = 'badCertificateCallback',
|
badCertificateCallback = 'badCertificateCallback',
|
||||||
continuePlayingPart = 'continuePlayingPart',
|
continuePlayingPart = 'continuePlayingPart',
|
||||||
cdnSpeedTest = 'cdnSpeedTest',
|
cdnSpeedTest = 'cdnSpeedTest',
|
||||||
|
horizontalPreview = 'horizontalPreview',
|
||||||
|
|
||||||
// Sponsor Block
|
// Sponsor Block
|
||||||
enableSponsorBlock = 'enableSponsorBlock',
|
enableSponsorBlock = 'enableSponsorBlock',
|
||||||
|
|||||||
Reference in New Issue
Block a user