:sparkless: Add comics to download list
This commit is contained in:
parent
ccae23ed75
commit
2c30885bc3
|
@ -1014,13 +1014,86 @@ class PkzComicViewLog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IsPro {
|
class ProInfoAll {
|
||||||
late bool isPro;
|
ProInfoAll({
|
||||||
late int expire;
|
required this.proInfoAf,
|
||||||
|
required this.proInfoPat,
|
||||||
|
});
|
||||||
|
late final ProInfoAf proInfoAf;
|
||||||
|
late final ProInfoPat proInfoPat;
|
||||||
|
|
||||||
IsPro.fromJson(Map<String, dynamic> json) {
|
ProInfoAll.fromJson(Map<String, dynamic> json){
|
||||||
this.isPro = json["isPro"];
|
proInfoAf = ProInfoAf.fromJson(json['pro_info_af']);
|
||||||
this.expire = json["expire"];
|
proInfoPat = ProInfoPat.fromJson(json['pro_info_pat']);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final _data = <String, dynamic>{};
|
||||||
|
_data['pro_info_normal'] = proInfoAf.toJson();
|
||||||
|
_data['pro_info_pat'] = proInfoPat.toJson();
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProInfoAf {
|
||||||
|
ProInfoAf({
|
||||||
|
required this.isPro,
|
||||||
|
required this.expire,
|
||||||
|
});
|
||||||
|
late final bool isPro;
|
||||||
|
late final int expire;
|
||||||
|
|
||||||
|
ProInfoAf.fromJson(Map<String, dynamic> json){
|
||||||
|
isPro = json['is_pro'];
|
||||||
|
expire = json['expire'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final _data = <String, dynamic>{};
|
||||||
|
_data['is_pro'] = isPro;
|
||||||
|
_data['expire'] = expire;
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProInfoPat {
|
||||||
|
ProInfoPat({
|
||||||
|
required this.isPro,
|
||||||
|
required this.patId,
|
||||||
|
required this.bindUid,
|
||||||
|
required this.requestDelete,
|
||||||
|
required this.reBind,
|
||||||
|
required this.errorType,
|
||||||
|
required this.errorMsg,
|
||||||
|
});
|
||||||
|
late final bool isPro;
|
||||||
|
late final String patId;
|
||||||
|
late final String bindUid;
|
||||||
|
late final int requestDelete;
|
||||||
|
late final int reBind;
|
||||||
|
late final int errorType;
|
||||||
|
late final String errorMsg;
|
||||||
|
|
||||||
|
ProInfoPat.fromJson(Map<String, dynamic> json){
|
||||||
|
isPro = json['is_pro'];
|
||||||
|
patId = json['pat_id'];
|
||||||
|
bindUid = json['bind_uid'];
|
||||||
|
requestDelete = json['request_delete'];
|
||||||
|
reBind = json['re_bind'];
|
||||||
|
errorType = json['error_type'];
|
||||||
|
errorMsg = json['error_msg'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final _data = <String, dynamic>{};
|
||||||
|
_data['is_pro'] = isPro;
|
||||||
|
_data['pat_id'] = patId;
|
||||||
|
_data['bind_uid'] = bindUid;
|
||||||
|
_data['request_delete'] = requestDelete;
|
||||||
|
_data['re_bind'] = reBind;
|
||||||
|
_data['error_type'] = errorType;
|
||||||
|
_data['error_msg'] = errorMsg;
|
||||||
|
return _data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -910,8 +910,8 @@ class Method {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<IsPro> isPro() async {
|
Future<ProInfoAll> proInfoAll() async {
|
||||||
return IsPro.fromJson(jsonDecode(await _flatInvoke("isPro", "")));
|
return ProInfoAll.fromJson(jsonDecode(await _flatInvoke("proInfoAll", "")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future reloadPro() {
|
Future reloadPro() {
|
||||||
|
@ -1009,4 +1009,8 @@ class Method {
|
||||||
Future androidMkdirs(String path) async {
|
Future androidMkdirs(String path) async {
|
||||||
return await _channel.invokeMethod("androidMkdirs", path);
|
return await _channel.invokeMethod("androidMkdirs", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future downloadAll(List<String> comicIds) {
|
||||||
|
return _flatInvoke("downloadAll", comicIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import 'package:event/event.dart';
|
import 'package:event/event.dart';
|
||||||
import 'package:pikapika/basic/Method.dart';
|
import 'package:pikapika/basic/Method.dart';
|
||||||
|
|
||||||
var isPro = false;
|
import '../Entities.dart';
|
||||||
var isProEx = 0;
|
|
||||||
|
bool get isPro {
|
||||||
|
return _proInfoAll.proInfoAf.isPro || _proInfoAll.proInfoPat.isPro;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProInfoAf get proInfoAf => _proInfoAll.proInfoAf;
|
||||||
|
ProInfoPat get proInfoPat => _proInfoAll.proInfoPat;
|
||||||
|
|
||||||
final proEvent = Event();
|
final proEvent = Event();
|
||||||
|
late ProInfoAll _proInfoAll;
|
||||||
|
|
||||||
Future reloadIsPro() async {
|
Future reloadIsPro() async {
|
||||||
final p = await method.isPro();
|
_proInfoAll = await method.proInfoAll();
|
||||||
isPro = p.isPro;
|
|
||||||
isProEx = p.expire;
|
|
||||||
proEvent.broadcast();
|
proEvent.broadcast();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_search_bar/flutter_search_bar.dart';
|
import 'package:flutter_search_bar/flutter_search_bar.dart';
|
||||||
import 'package:pikapika/basic/Common.dart';
|
import 'package:pikapika/basic/Common.dart';
|
||||||
|
import 'package:pikapika/basic/config/PagerAction.dart';
|
||||||
import 'package:pikapika/basic/config/ShadowCategories.dart';
|
import 'package:pikapika/basic/config/ShadowCategories.dart';
|
||||||
import 'package:pikapika/basic/config/ShadowCategoriesMode.dart';
|
import 'package:pikapika/basic/config/ShadowCategoriesMode.dart';
|
||||||
import 'package:pikapika/basic/store/Categories.dart';
|
import 'package:pikapika/basic/store/Categories.dart';
|
||||||
import 'package:pikapika/basic/config/ListLayout.dart';
|
|
||||||
import 'package:pikapika/basic/Method.dart';
|
import 'package:pikapika/basic/Method.dart';
|
||||||
|
import 'package:pikapika/screens/components/ComicList.dart';
|
||||||
import '../basic/Entities.dart';
|
import '../basic/Entities.dart';
|
||||||
import '../basic/config/Address.dart';
|
import '../basic/config/Address.dart';
|
||||||
import '../basic/config/IconLoading.dart';
|
import '../basic/config/IconLoading.dart';
|
||||||
import 'SearchScreen.dart';
|
import 'SearchScreen.dart';
|
||||||
import 'components/ComicPager.dart';
|
import 'components/ComicPager.dart';
|
||||||
import 'components/Common.dart';
|
import 'components/Common.dart';
|
||||||
|
import 'components/GoDownloadSelect.dart';
|
||||||
import 'components/RightClickPop.dart';
|
import 'components/RightClickPop.dart';
|
||||||
|
|
||||||
// 漫画列表
|
// 漫画列表
|
||||||
|
@ -36,6 +38,7 @@ class ComicsScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ComicsScreenState extends State<ComicsScreen> {
|
class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
|
late final _comicListController = ComicListController();
|
||||||
late final SearchBar _categorySearchBar = SearchBar(
|
late final SearchBar _categorySearchBar = SearchBar(
|
||||||
hintText: '搜索分类 - ${categoryTitle(widget.category)}',
|
hintText: '搜索分类 - ${categoryTitle(widget.category)}',
|
||||||
inBar: false,
|
inBar: false,
|
||||||
|
@ -55,7 +58,11 @@ class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
title: Text(categoryTitle(widget.category)),
|
title: Text(categoryTitle(widget.category)),
|
||||||
actions: [
|
actions: [
|
||||||
commonPopMenu(context),
|
commonPopMenu(
|
||||||
|
context,
|
||||||
|
setState: setState,
|
||||||
|
comicListController: _comicListController,
|
||||||
|
),
|
||||||
addressPopMenu(context),
|
addressPopMenu(context),
|
||||||
_chooseCategoryAction(),
|
_chooseCategoryAction(),
|
||||||
_categorySearchBar.getSearchAction(context),
|
_categorySearchBar.getSearchAction(context),
|
||||||
|
@ -104,6 +111,12 @@ class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<ComicsPage> _load(String _currentSort, int _currentPage) {
|
Future<ComicsPage> _load(String _currentSort, int _currentPage) {
|
||||||
|
if (currentPagerAction() == PagerAction.CONTROLLER &&
|
||||||
|
_comicListController.selecting) {
|
||||||
|
setState(() {
|
||||||
|
_comicListController.selecting = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
return method.comics(
|
return method.comics(
|
||||||
_currentSort,
|
_currentSort,
|
||||||
_currentPage,
|
_currentPage,
|
||||||
|
@ -115,7 +128,7 @@ class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context){
|
Widget build(BuildContext context) {
|
||||||
return rightClickPop(
|
return rightClickPop(
|
||||||
child: buildScreen(context),
|
child: buildScreen(context),
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -154,10 +167,15 @@ class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_comicListController.selecting) {
|
||||||
|
appBar = downAppBar(context, _comicListController, setState);
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: appBar,
|
appBar: appBar,
|
||||||
body: ComicPager(
|
body: ComicPager(
|
||||||
fetchPage: _load,
|
fetchPage: _load,
|
||||||
|
comicListController: _comicListController,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,13 +68,26 @@ class _ProScreenState extends State<ProScreen> {
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("发电详情"),
|
title: const Text("签到或礼物卡"),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
isPro
|
proInfoAf.isPro
|
||||||
? "发电中 (${DateTime.fromMillisecondsSinceEpoch(1000 * isProEx).toString()})"
|
? "发电中 (${DateTime.fromMillisecondsSinceEpoch(1000 * proInfoAf.expire).toString()})"
|
||||||
: "未发电",
|
: "未发电",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
...(proInfoPat.patId.isNotEmpty ? [
|
||||||
|
ListTile(
|
||||||
|
onTap: () {
|
||||||
|
managementPat();
|
||||||
|
},
|
||||||
|
title: const Text("P站支持"),
|
||||||
|
subtitle: Text((
|
||||||
|
proInfoPat.isPro
|
||||||
|
? "发电中"
|
||||||
|
: "未发电") + ("(点击管理)"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] : []),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("我曾经发过电"),
|
title: const Text("我曾经发过电"),
|
||||||
|
@ -111,4 +124,8 @@ class _ProScreenState extends State<ProScreen> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void managementPat() {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,17 +13,34 @@ import 'ComicInfoCard.dart';
|
||||||
import 'Images.dart';
|
import 'Images.dart';
|
||||||
import 'LinkToComicInfo.dart';
|
import 'LinkToComicInfo.dart';
|
||||||
|
|
||||||
|
class ComicListController {
|
||||||
|
_ComicListState? _state;
|
||||||
|
|
||||||
|
bool get selecting => _state?._selecting ?? false;
|
||||||
|
|
||||||
|
set selecting(bool value) => _state?._setSelect(value);
|
||||||
|
|
||||||
|
List<String> get selected => _state?._selected ?? [];
|
||||||
|
|
||||||
|
selectAll() {
|
||||||
|
_state?._selectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 漫画列表页
|
// 漫画列表页
|
||||||
class ComicList extends StatefulWidget {
|
class ComicList extends StatefulWidget {
|
||||||
final Widget? appendWidget;
|
final Widget? appendWidget;
|
||||||
final List<ComicSimple> comicList;
|
final List<ComicSimple> comicList;
|
||||||
final ScrollController? controller;
|
final ScrollController? scrollController;
|
||||||
|
final ComicListController? listController;
|
||||||
|
|
||||||
const ComicList(
|
const ComicList(
|
||||||
this.comicList, {
|
this.comicList, {
|
||||||
this.appendWidget,
|
this.appendWidget,
|
||||||
this.controller,
|
this.scrollController,
|
||||||
Key? key,
|
Key? key,
|
||||||
|
// required
|
||||||
|
this.listController,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -32,6 +49,25 @@ class ComicList extends StatefulWidget {
|
||||||
|
|
||||||
class _ComicListState extends State<ComicList> {
|
class _ComicListState extends State<ComicList> {
|
||||||
final List<String> viewedList = [];
|
final List<String> viewedList = [];
|
||||||
|
bool _selecting = false;
|
||||||
|
List<String> _selected = [];
|
||||||
|
|
||||||
|
_selectAll() {
|
||||||
|
setState(() {
|
||||||
|
if (_selected.length == widget.comicList.length) {
|
||||||
|
_selected.clear();
|
||||||
|
} else {
|
||||||
|
_selected.addAll(widget.comicList.map((e) => e.id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_setSelect(bool value) {
|
||||||
|
setState(() {
|
||||||
|
_selected.clear();
|
||||||
|
_selecting = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future _loadViewed() async {
|
Future _loadViewed() async {
|
||||||
if (widget.comicList.isNotEmpty) {
|
if (widget.comicList.isNotEmpty) {
|
||||||
|
@ -43,6 +79,7 @@ class _ComicListState extends State<ComicList> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
widget.listController?._state = this;
|
||||||
_loadViewed();
|
_loadViewed();
|
||||||
listLayoutEvent.subscribe(_onLayoutChange);
|
listLayoutEvent.subscribe(_onLayoutChange);
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -50,6 +87,9 @@ class _ComicListState extends State<ComicList> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (widget.listController?._state == this) {
|
||||||
|
widget.listController?._state = null;
|
||||||
|
}
|
||||||
listLayoutEvent.unsubscribe(_onLayoutChange);
|
listLayoutEvent.unsubscribe(_onLayoutChange);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +114,7 @@ class _ComicListState extends State<ComicList> {
|
||||||
|
|
||||||
Widget _buildInfoCardList() {
|
Widget _buildInfoCardList() {
|
||||||
return ListView(
|
return ListView(
|
||||||
controller: widget.controller,
|
controller: widget.scrollController,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
children: [
|
children: [
|
||||||
...widget.comicList.map((e) {
|
...widget.comicList.map((e) {
|
||||||
|
@ -122,6 +162,42 @@ class _ComicListState extends State<ComicList> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (_selecting) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
if (_selected.contains(e.id)) {
|
||||||
|
_selected.remove(e.id);
|
||||||
|
} else {
|
||||||
|
_selected.add(e.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Stack(children: [
|
||||||
|
AbsorbPointer(
|
||||||
|
child: LinkToComicInfo(
|
||||||
|
comicId: e.id,
|
||||||
|
child: ComicInfoCard(
|
||||||
|
e,
|
||||||
|
viewed: viewedList.contains(e.id),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(children: [
|
||||||
|
Expanded(child: Container()),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: Icon(
|
||||||
|
_selected.contains(e.id)
|
||||||
|
? Icons.check_circle_sharp
|
||||||
|
: Icons.circle_outlined,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
return LinkToComicInfo(
|
return LinkToComicInfo(
|
||||||
comicId: e.id,
|
comicId: e.id,
|
||||||
child: ComicInfoCard(
|
child: ComicInfoCard(
|
||||||
|
@ -242,7 +318,7 @@ class _ComicListState extends State<ComicList> {
|
||||||
}
|
}
|
||||||
// 返回
|
// 返回
|
||||||
return ListView(
|
return ListView(
|
||||||
controller: widget.controller,
|
controller: widget.scrollController,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
padding: EdgeInsets.only(top: gap, bottom: gap),
|
padding: EdgeInsets.only(top: gap, bottom: gap),
|
||||||
children: wraps,
|
children: wraps,
|
||||||
|
@ -380,7 +456,7 @@ class _ComicListState extends State<ComicList> {
|
||||||
}
|
}
|
||||||
// 返回
|
// 返回
|
||||||
return ListView(
|
return ListView(
|
||||||
controller: widget.controller,
|
controller: widget.scrollController,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
padding: EdgeInsets.only(top: gap, bottom: gap),
|
padding: EdgeInsets.only(top: gap, bottom: gap),
|
||||||
children: wraps,
|
children: wraps,
|
||||||
|
|
|
@ -14,9 +14,15 @@ import 'ContentLoading.dart';
|
||||||
|
|
||||||
// 漫画列页
|
// 漫画列页
|
||||||
class ComicPager extends StatefulWidget {
|
class ComicPager extends StatefulWidget {
|
||||||
|
final ComicListController? comicListController;
|
||||||
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
||||||
|
|
||||||
const ComicPager({required this.fetchPage, Key? key}) : super(key: key);
|
const ComicPager({
|
||||||
|
required this.fetchPage,
|
||||||
|
Key? key,
|
||||||
|
// required
|
||||||
|
this.comicListController,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _ComicPagerState();
|
State<StatefulWidget> createState() => _ComicPagerState();
|
||||||
|
@ -43,9 +49,15 @@ class _ComicPagerState extends State<ComicPager> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
switch (currentPagerAction()) {
|
switch (currentPagerAction()) {
|
||||||
case PagerAction.CONTROLLER:
|
case PagerAction.CONTROLLER:
|
||||||
return ControllerComicPager(fetchPage: widget.fetchPage);
|
return ControllerComicPager(
|
||||||
|
fetchPage: widget.fetchPage,
|
||||||
|
comicListController: widget.comicListController,
|
||||||
|
);
|
||||||
case PagerAction.STREAM:
|
case PagerAction.STREAM:
|
||||||
return StreamComicPager(fetchPage: widget.fetchPage);
|
return StreamComicPager(
|
||||||
|
fetchPage: widget.fetchPage,
|
||||||
|
comicListController: widget.comicListController,
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
@ -53,11 +65,13 @@ class _ComicPagerState extends State<ComicPager> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ControllerComicPager extends StatefulWidget {
|
class ControllerComicPager extends StatefulWidget {
|
||||||
|
final ComicListController? comicListController;
|
||||||
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
||||||
|
|
||||||
const ControllerComicPager({
|
const ControllerComicPager({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.fetchPage,
|
required this.fetchPage,
|
||||||
|
required this.comicListController,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -107,6 +121,7 @@ class _ControllerComicPagerState extends State<ControllerComicPager> {
|
||||||
body: ComicList(
|
body: ComicList(
|
||||||
comicsPage.docs,
|
comicsPage.docs,
|
||||||
appendWidget: _buildNextButton(comicsPage),
|
appendWidget: _buildNextButton(comicsPage),
|
||||||
|
listController: widget.comicListController,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -255,11 +270,13 @@ class _ControllerComicPagerState extends State<ControllerComicPager> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class StreamComicPager extends StatefulWidget {
|
class StreamComicPager extends StatefulWidget {
|
||||||
|
final ComicListController? comicListController;
|
||||||
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
||||||
|
|
||||||
const StreamComicPager({
|
const StreamComicPager({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.fetchPage,
|
required this.fetchPage,
|
||||||
|
required this.comicListController,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -351,8 +368,9 @@ class _StreamComicPagerState extends State<StreamComicPager> {
|
||||||
appBar: _buildAppBar(context),
|
appBar: _buildAppBar(context),
|
||||||
body: ComicList(
|
body: ComicList(
|
||||||
_list,
|
_list,
|
||||||
controller: _scrollController,
|
scrollController: _scrollController,
|
||||||
appendWidget: _buildLoadingCell(),
|
appendWidget: _buildLoadingCell(),
|
||||||
|
listController: widget.comicListController,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pikapika/screens/components/ComicList.dart';
|
||||||
|
|
||||||
|
import '../../basic/config/IsPro.dart';
|
||||||
import '../../basic/config/ListLayout.dart';
|
import '../../basic/config/ListLayout.dart';
|
||||||
import '../../basic/config/ShadowCategories.dart';
|
import '../../basic/config/ShadowCategories.dart';
|
||||||
import '../../basic/config/ShadowCategoriesMode.dart';
|
import '../../basic/config/ShadowCategoriesMode.dart';
|
||||||
|
|
||||||
Widget commonPopMenu(BuildContext context) {
|
Widget commonPopMenu(
|
||||||
|
BuildContext context, {
|
||||||
|
ComicListController? comicListController,
|
||||||
|
void Function(VoidCallback fn)? setState,
|
||||||
|
}) {
|
||||||
return PopupMenuButton<int>(
|
return PopupMenuButton<int>(
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuItem<int>>[
|
itemBuilder: (BuildContext context) => <PopupMenuItem<int>>[
|
||||||
const PopupMenuItem<int>(
|
const PopupMenuItem<int>(
|
||||||
|
@ -28,6 +34,22 @@ Widget commonPopMenu(BuildContext context) {
|
||||||
title: Text("封印列表"),
|
title: Text("封印列表"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
...comicListController != null && setState != null
|
||||||
|
? [
|
||||||
|
PopupMenuItem<int>(
|
||||||
|
value: 3,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.download),
|
||||||
|
title: Text(
|
||||||
|
"下载" + (isPro ? "" : "Pro"),
|
||||||
|
style: TextStyle(
|
||||||
|
color: isPro ? null : Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
: [],
|
||||||
],
|
],
|
||||||
onSelected: (int value) {
|
onSelected: (int value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
@ -40,6 +62,15 @@ Widget commonPopMenu(BuildContext context) {
|
||||||
case 2:
|
case 2:
|
||||||
chooseShadowCategories(context);
|
chooseShadowCategories(context);
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
if (setState != null) {
|
||||||
|
if (comicListController != null) {
|
||||||
|
setState(() {
|
||||||
|
comicListController.selecting = !comicListController.selecting;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../basic/Channels.dart';
|
||||||
|
import '../../basic/Common.dart';
|
||||||
|
import '../../basic/Method.dart';
|
||||||
|
import 'ContentLoading.dart';
|
||||||
|
|
||||||
|
class DownloadComicsScreen extends StatefulWidget {
|
||||||
|
final List<String> comicIds;
|
||||||
|
|
||||||
|
const DownloadComicsScreen(this.comicIds, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _DownloadComicsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DownloadComicsScreenState extends State<DownloadComicsScreen> {
|
||||||
|
bool exporting = false;
|
||||||
|
bool exported = false;
|
||||||
|
bool exportFail = false;
|
||||||
|
dynamic e;
|
||||||
|
String exportMessage = "正在创建下载任务";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
registerEvent(_onMessageChange, "EXPORT");
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
unregisterEvent(_onMessageChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onMessageChange(event) {
|
||||||
|
setState(() {
|
||||||
|
exportMessage = event;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("批量下载"),
|
||||||
|
),
|
||||||
|
body: _body(),
|
||||||
|
),
|
||||||
|
onWillPop: () async {
|
||||||
|
if (exporting) {
|
||||||
|
defaultToast(context, "创建下载任务中, 请稍后");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _body() {
|
||||||
|
if (exporting) {
|
||||||
|
return ContentLoading(label: exportMessage);
|
||||||
|
}
|
||||||
|
if (exportFail) {
|
||||||
|
return Center(child: Text("失败\n$e"));
|
||||||
|
}
|
||||||
|
if (exported) {
|
||||||
|
return const Center(child: Text("成功"));
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
Container(height: 20),
|
||||||
|
Container(height: 20),
|
||||||
|
_buildButtonInner("您即将下载${widget.comicIds.length}部漫画, 如果漫画已经存在, 则补充新增加的章节"),
|
||||||
|
Container(height: 20),
|
||||||
|
Container(height: 20),
|
||||||
|
MaterialButton(
|
||||||
|
onPressed: _create,
|
||||||
|
child: _buildButtonInner("确认"),
|
||||||
|
),
|
||||||
|
Container(height: 20),
|
||||||
|
Container(height: 20),
|
||||||
|
Container(height: 20),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_create() async {
|
||||||
|
var name = "";
|
||||||
|
try {
|
||||||
|
setState(() {
|
||||||
|
exporting = true;
|
||||||
|
});
|
||||||
|
await method.downloadAll(
|
||||||
|
widget.comicIds,
|
||||||
|
);
|
||||||
|
exported = true;
|
||||||
|
} catch (err) {
|
||||||
|
e = err;
|
||||||
|
exportFail = true;
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
exporting = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildButtonInner(String text) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
return Container(
|
||||||
|
width: constraints.maxWidth,
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
color: (Theme.of(context).textTheme.bodyText1?.color ?? Colors.black)
|
||||||
|
.withOpacity(.05),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pikapika/basic/Common.dart';
|
||||||
|
import 'package:pikapika/screens/components/ComicList.dart';
|
||||||
|
|
||||||
|
import 'DownloadComicsScreen.dart';
|
||||||
|
|
||||||
|
AppBar downAppBar(
|
||||||
|
BuildContext context,
|
||||||
|
ComicListController _comicListController,
|
||||||
|
void Function(VoidCallback fn) setState,
|
||||||
|
) {
|
||||||
|
return AppBar(
|
||||||
|
actions: [
|
||||||
|
MaterialButton(
|
||||||
|
minWidth: 0,
|
||||||
|
onPressed: () async {
|
||||||
|
setState(() {
|
||||||
|
_comicListController.selecting = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Container()),
|
||||||
|
const Icon(
|
||||||
|
Icons.cancel_outlined,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.white),
|
||||||
|
),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MaterialButton(
|
||||||
|
minWidth: 0,
|
||||||
|
onPressed: () async {
|
||||||
|
_comicListController.selectAll();
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Container()),
|
||||||
|
const Icon(
|
||||||
|
Icons.select_all,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'全选',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.white),
|
||||||
|
),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MaterialButton(
|
||||||
|
minWidth: 0,
|
||||||
|
onPressed: () async {
|
||||||
|
// todo
|
||||||
|
final list = _comicListController.selected;
|
||||||
|
if (list.isEmpty) {
|
||||||
|
defaultToast(context, "请选择漫画");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_comicListController.selecting = false;
|
||||||
|
Navigator.of(context).push(MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return DownloadComicsScreen(list);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Container()),
|
||||||
|
const Icon(
|
||||||
|
Icons.check,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'确认',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.white),
|
||||||
|
),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue