From 4a2489dcf4b6ccd7401e6996b034596440ca42dc Mon Sep 17 00:00:00 2001 From: niuhuan Date: Wed, 12 Apr 2023 15:53:37 +0800 Subject: [PATCH] :recycle: Permisson rewrite --- README.md | 16 +- android/app/src/main/AndroidManifest.xml | 1 + ci/version.info.txt | 3 + lib/basic/Common.dart | 13 +- lib/basic/Cross.dart | 10 +- lib/basic/Entities.dart | 4 + lib/basic/Method.dart | 16 ++ lib/basic/config/ChooserRoot.dart | 9 +- lib/basic/config/ExportPath.dart | 9 +- lib/screens/AccessKeyReplaceScreen.dart | 75 +++++++ lib/screens/ComicsScreen.dart | 6 +- lib/screens/InitScreen.dart | 35 +++- lib/screens/PkzArchiveScreen.dart | 14 +- lib/screens/ProScreen.dart | 200 ++++++++++++++++--- lib/screens/SearchScreen.dart | 17 +- lib/screens/components/Common.dart | 12 +- lib/screens/components/GoDownloadSelect.dart | 8 +- pubspec.lock | 18 +- pubspec.yaml | 1 + 19 files changed, 393 insertions(+), 74 deletions(-) create mode 100644 lib/screens/AccessKeyReplaceScreen.dart diff --git a/README.md b/README.md index b29f531..71d94b0 100644 --- a/README.md +++ b/README.md @@ -89,21 +89,11 @@ VPN->代理->分流, 这三个功能如果同时设置, 您会在您手机的VPN ## 请您遵守使用规则 -本文中提到的本软件拓展包括但是不限于以下内容 +软件副本分发以及代码使用规则 -- 使用本软件进行继续开发形成的软件。 -- 引入本软件部分内容为依赖/使用本软件内代码的同时包含本软件内一致内容或功能。 -- 直接对本软件进行打包发布 - -软件副本分发以及代码使用规则规则 - -- 本软件仅供学习交流使用, 本软件或本软件的拓展, 个人或企业不可用于商业用途, 不可上架任何商店。 -- 本软件的拓展在未经允许的情况下可以自用但不允许释放任何releases。 +- 本软件的代码在未经允许的情况下可以自用但不允许释放任何releases, 个人或企业不可用于商业用途, 不可上架任何商店。。 - 不要在任何其他 **二次元软件** 的 **聊天社区** 或 **开发社区** 内, 发布有关本软件的链接或信息, 对于观点不同产生的分歧作者不站队任何立场。 -- 不要发送本软件安装包到 **任何社区内** , 不要将APK/IPA/ZIP/DMG发送包括任何聊天软件内的群聊功能。 分享本软件时, 在社区中使用Github中提供的Releases页面的链接, 或使用私聊窗口发送。 - -源代码使用规则 - +- 不要发送本软件安装包到 **任何社区内** , 不要将APK/IPA/ZIP/DMG发送包括任何聊天软件内的群聊功能。 请使用Github中提供的Releases页面的链接。 - 对本仓库的fork需要保留本仓库的链接, 以引导用户在主要仓库进行讨论。 责任声明 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1e6b4a7..6cac205 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + diff --git a/ci/version.info.txt b/ci/version.info.txt index 3bb7d4e..ba3c04f 100644 --- a/ci/version.info.txt +++ b/ci/version.info.txt @@ -1,3 +1,6 @@ v1.7.2 - [x] 🐛 修复安卓13导入导出的问题 - [x] 🐛 修复测速不好用的问题 +- [x] ♻️ 梳理一些权限 +- [x] ✨ 增加批量下载功能 +- [x] ✨ 增加PAT入会发电 diff --git a/lib/basic/Common.dart b/lib/basic/Common.dart index f8183bc..eb3fe86 100644 --- a/lib/basic/Common.dart +++ b/lib/basic/Common.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_styled_toast/flutter_styled_toast.dart'; +import 'package:pikapika/screens/AccessKeyReplaceScreen.dart'; import 'package:uni_links/uni_links.dart'; import 'package:uri_to_file/uri_to_file.dart'; @@ -300,7 +301,17 @@ StreamSubscription linkSubscript(BuildContext context) { return linkStream.listen((uri) async { if (uri == null) return; var parsed = Uri.parse(uri); - if (RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(uri).isNotEmpty) { + if (RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$").allMatches(uri).isNotEmpty) { + String accessKey = RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$") + .allMatches(uri) + .first + .group(1)!; + Navigator.of(context).push( + mixRoute( + builder: (BuildContext context) => AccessKeyReplaceScreen(accessKey: accessKey), + ), + ); + } else if (RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(uri).isNotEmpty) { String comicId = RegExp(r"^pika://comic/([0-9A-z]+)/$") .allMatches(uri) .first diff --git a/lib/basic/Cross.dart b/lib/basic/Cross.dart index c13859a..5b76bd2 100644 --- a/lib/basic/Cross.dart +++ b/lib/basic/Cross.dart @@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:pikapika/basic/Common.dart'; +import 'package:pikapika/basic/config/Platform.dart'; import 'package:url_launcher/url_launcher.dart'; import 'Method.dart'; @@ -68,8 +69,13 @@ Future saveImageQuiet(String path, BuildContext context) async { } Future _saveImageAndroid(String path, BuildContext context) async { - var p = await Permission.storage.request(); - if (!p.isGranted) { + late bool g; + if (androidVersion < 30) { + g = await Permission.storage.request().isGranted; + }else{ + g = await Permission.manageExternalStorage.request().isGranted; + } + if (!g) { return; } return method.androidSaveFileToImage(path); diff --git a/lib/basic/Entities.dart b/lib/basic/Entities.dart index a176358..5017479 100644 --- a/lib/basic/Entities.dart +++ b/lib/basic/Entities.dart @@ -1065,6 +1065,7 @@ class ProInfoPat { required this.reBind, required this.errorType, required this.errorMsg, + required this.accessKey, }); late final bool isPro; late final String patId; @@ -1073,6 +1074,7 @@ class ProInfoPat { late final int reBind; late final int errorType; late final String errorMsg; + late final String accessKey; ProInfoPat.fromJson(Map json){ isPro = json['is_pro']; @@ -1082,6 +1084,7 @@ class ProInfoPat { reBind = json['re_bind']; errorType = json['error_type']; errorMsg = json['error_msg']; + accessKey = json['access_key']; } Map toJson() { @@ -1093,6 +1096,7 @@ class ProInfoPat { _data['re_bind'] = reBind; _data['error_type'] = errorType; _data['error_msg'] = errorMsg; + _data['access_key'] = accessKey; return _data; } } diff --git a/lib/basic/Method.dart b/lib/basic/Method.dart index 0b0d0c1..635d2f0 100644 --- a/lib/basic/Method.dart +++ b/lib/basic/Method.dart @@ -1013,4 +1013,20 @@ class Method { Future downloadAll(List comicIds) { return _flatInvoke("downloadAll", comicIds); } + + Future setPatAccessKey(String accessKey) { + return _flatInvoke("setPatAccessKey", accessKey); + } + + Future reloadPatAccount() { + return _flatInvoke("reloadPatAccount", ""); + } + + Future bindThisAccount() { + return _flatInvoke("bindThisAccount", ""); + } + + Future clearPat() { + return _flatInvoke("clearPat", ""); + } } diff --git a/lib/basic/config/ChooserRoot.dart b/lib/basic/config/ChooserRoot.dart index c7b75e4..d40e1b4 100644 --- a/lib/basic/config/ChooserRoot.dart +++ b/lib/basic/config/ChooserRoot.dart @@ -7,6 +7,7 @@ import 'package:permission_handler/permission_handler.dart'; import '../Common.dart'; import '../Method.dart'; +import 'Platform.dart'; const _propertyName = "chooserRoot"; late String _chooserRoot; @@ -30,7 +31,13 @@ Future initChooserRoot() async { Future currentChooserRoot() async { if (Platform.isAndroid) { - if (!(await Permission.storage.request()).isGranted) { + late bool g; + if (androidVersion < 30) { + g = await Permission.storage.request().isGranted; + }else{ + g = await Permission.manageExternalStorage.request().isGranted; + } + if (!g) { throw Exception("申请权限被拒绝"); } } diff --git a/lib/basic/config/ExportPath.dart b/lib/basic/config/ExportPath.dart index c0c546a..052bcc1 100644 --- a/lib/basic/config/ExportPath.dart +++ b/lib/basic/config/ExportPath.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; import '../Cross.dart'; import '../Method.dart'; +import 'Platform.dart'; const _propertyName = "exportPath"; late String _exportPath; @@ -35,7 +36,13 @@ Future attachExportPath() async { path = await method.iosGetDocumentDir(); } else { if (Platform.isAndroid) { - if (!(await Permission.storage.request()).isGranted) { + late bool g; + if (androidVersion < 30) { + g = await Permission.storage.request().isGranted; + }else{ + g = await Permission.manageExternalStorage.request().isGranted; + } + if (!g) { throw Exception("申请权限被拒绝"); } } diff --git a/lib/screens/AccessKeyReplaceScreen.dart b/lib/screens/AccessKeyReplaceScreen.dart new file mode 100644 index 0000000..c84d65a --- /dev/null +++ b/lib/screens/AccessKeyReplaceScreen.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:pikapika/basic/Method.dart'; +import 'package:pikapika/screens/components/ContentLoading.dart'; + +import '../basic/config/IsPro.dart'; + +class AccessKeyReplaceScreen extends StatefulWidget { + final String accessKey; + + const AccessKeyReplaceScreen({Key? key, required this.accessKey}) + : super(key: key); + + @override + State createState() => _AccessKeyReplaceScreenState(); +} + +class _AccessKeyReplaceScreenState extends State { + var _loading = false; + var _message = ""; + var _success = false; + + _set() async { + setState(() { + _loading = true; + }); + try { + await method.setPatAccessKey(widget.accessKey); + await reloadIsPro(); + _success = true; + } catch (e) { + _message = "错误 : $e"; + } finally { + setState(() { + _loading = false; + }); + } + } + + Widget _content() { + if (_loading) { + return const ContentLoading(label: "加载中"); + } + if (_success) { + return const Text("您的赞助登录成功, 请返回"); + } + return Column( + children: [ + Expanded(child: Container()), + Text(widget.accessKey), + Text(_message), + Container( + height: 10, + ), + MaterialButton( + color: Colors.grey, + onPressed: _set, + child: const Text("确认"), + ), + Expanded(child: Container()), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("更换PAT账户"), + ), + body: Center( + child: _content(), + ), + ); + } +} diff --git a/lib/screens/ComicsScreen.dart b/lib/screens/ComicsScreen.dart index a40a372..091e414 100644 --- a/lib/screens/ComicsScreen.dart +++ b/lib/screens/ComicsScreen.dart @@ -160,7 +160,11 @@ class _ComicsScreenState extends State { appBar = AppBar( title: Text(title), actions: [ - commonPopMenu(context), + commonPopMenu( + context, + setState: setState, + comicListController: _comicListController, + ), addressPopMenu(context), _chooseCategoryAction(), ], diff --git a/lib/screens/InitScreen.dart b/lib/screens/InitScreen.dart index ad0fced..9b75a7e 100644 --- a/lib/screens/InitScreen.dart +++ b/lib/screens/InitScreen.dart @@ -37,6 +37,7 @@ import 'package:pikapika/basic/config/Version.dart'; import 'package:pikapika/basic/config/VolumeController.dart'; import 'package:pikapika/basic/config/ShadowCategoriesMode.dart'; import 'package:pikapika/basic/config/WillPopNotice.dart'; +import 'package:pikapika/screens/AccessKeyReplaceScreen.dart'; import 'package:pikapika/screens/ComicInfoScreen.dart'; import 'package:pikapika/screens/PkzArchiveScreen.dart'; import 'package:uni_links/uni_links.dart'; @@ -126,15 +127,37 @@ class _InitScreenState extends State { } if (initUrl != null) { var parsed = Uri.parse(initUrl!); - if (RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(initUrl!).isNotEmpty) { - String comicId = RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(initUrl!).first.group(1)!; + if (RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$") + .allMatches(initUrl!) + .isNotEmpty) { + String accessKey = RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$") + .allMatches(initUrl!) + .first + .group(1)!; + Navigator.of(context).pushReplacement(mixRoute( + builder: (BuildContext context) => + AccessKeyReplaceScreen(accessKey: accessKey), + )); + return; + } else if (RegExp(r"^pika://comic/([0-9A-z]+)/$") + .allMatches(initUrl!) + .isNotEmpty) { + String comicId = RegExp(r"^pika://comic/([0-9A-z]+)/$") + .allMatches(initUrl!) + .first + .group(1)!; Navigator.of(context).pushReplacement(mixRoute( builder: (BuildContext context) => ComicInfoScreen(comicId: comicId, holdPkz: true), )); return; - } if (RegExp(r"^https?://pika/comic/([0-9A-z]+)/$").allMatches(initUrl!).isNotEmpty) { - String comicId = RegExp(r"^https?://pika/comic/([0-9A-z]+)/$").allMatches(initUrl!).first.group(1)!; + } else if (RegExp(r"^https?://pika/comic/([0-9A-z]+)/$") + .allMatches(initUrl!) + .isNotEmpty) { + String comicId = RegExp(r"^https?://pika/comic/([0-9A-z]+)/$") + .allMatches(initUrl!) + .first + .group(1)!; Navigator.of(context).pushReplacement(mixRoute( builder: (BuildContext context) => ComicInfoScreen(comicId: comicId, holdPkz: true), @@ -147,7 +170,9 @@ class _InitScreenState extends State { PkzArchiveScreen(pkzPath: file.path, holdPkz: true), )); return; - } else if (RegExp(r"^.*\.((pki)|(zip))$").allMatches(parsed.path).isNotEmpty) { + } else if (RegExp(r"^.*\.((pki)|(zip))$") + .allMatches(parsed.path) + .isNotEmpty) { File file = await toFile(initUrl!); Navigator.of(context).pushReplacement( mixRoute( diff --git a/lib/screens/PkzArchiveScreen.dart b/lib/screens/PkzArchiveScreen.dart index 935e7ee..c0a31ff 100644 --- a/lib/screens/PkzArchiveScreen.dart +++ b/lib/screens/PkzArchiveScreen.dart @@ -14,6 +14,7 @@ import 'package:uri_to_file/uri_to_file.dart'; import '../basic/Common.dart'; import '../basic/Navigator.dart'; import '../basic/config/IconLoading.dart'; +import '../basic/config/Platform.dart'; import 'PkzComicInfoScreen.dart'; class PkzArchiveScreen extends StatefulWidget { @@ -75,9 +76,16 @@ class _PkzArchiveScreenState extends State with RouteAware { Future _load() async { await method.viewPkz(_fileName, widget.pkzPath); - var p = await Permission.storage.request(); - if (!p.isGranted) { - throw 'error permission'; + if (Platform.isAndroid) { + late bool g; + if (androidVersion < 30) { + g = await Permission.storage.request().isGranted; + }else{ + g = await Permission.manageExternalStorage.request().isGranted; + } + if (!g) { + throw 'error permission'; + } } _info = await method.pkzInfo(widget.pkzPath); if (_info.comics.length == 1) { diff --git a/lib/screens/ProScreen.dart b/lib/screens/ProScreen.dart index 71413b2..7821c5d 100644 --- a/lib/screens/ProScreen.dart +++ b/lib/screens/ProScreen.dart @@ -1,7 +1,12 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:pikapika/basic/Common.dart'; import 'package:pikapika/basic/Method.dart'; +import 'package:pikapika/screens/AccessKeyReplaceScreen.dart'; +import '../basic/config/IconLoading.dart'; import '../basic/config/IsPro.dart'; class ProScreen extends StatefulWidget { @@ -21,9 +26,20 @@ class _ProScreenState extends State { _username = value; }); }); + proEvent.subscribe(_setState); super.initState(); } + @override + void dispose() { + proEvent.unsubscribe(_setState); + super.dispose(); + } + + _setState(_) { + setState(() {}); + } + @override Widget build(BuildContext context) { var size = MediaQuery.of(context).size; @@ -51,43 +67,25 @@ class _ProScreenState extends State { const Padding( padding: EdgeInsets.all(20), child: Text( - "点击\"我曾经发过电\"进同步发电状态\n" - "点击\"我刚才发了电\"兑换作者给您的礼物卡\n" - "去\"关于\"界面找到维护地址用爱发电", - ), - ), - const Divider(), - const Padding( - padding: EdgeInsets.all(20), - child: Text( - "发电小功能 \n" - " 多线程下载\n" - " 批量导入导出\n" - " 跳页", + "去\"关于\"界面找到维护地址可获得发电指引\n\n" + "1. \"签到/游戏/兑换\" \n" + " (1). \"我曾经发过电\"可同步相应发电状态\n" + " (2). \"我刚才发了电\"兑换作者给您的礼物卡\n" + "\n" + "2. \"PAT入会\"\n" + " 🔗将社区账号链接到软件, 同步成员状态, 订阅式发电" + "", ), ), const Divider(), ListTile( - title: const Text("签到或礼物卡"), + title: const Text("签到/游戏/兑换"), subtitle: Text( proInfoAf.isPro ? "发电中 (${DateTime.fromMillisecondsSinceEpoch(1000 * proInfoAf.expire).toString()})" : "未发电", ), ), - ...(proInfoPat.patId.isNotEmpty ? [ - ListTile( - onTap: () { - managementPat(); - }, - title: const Text("P站支持"), - subtitle: Text(( - proInfoPat.isPro - ? "发电中" - : "未发电") + ("(点击管理)"), - ), - ), - ] : []), const Divider(), ListTile( title: const Text("我曾经发过电"), @@ -120,12 +118,156 @@ class _ProScreenState extends State { }, ), const Divider(), + ...patPro(), + const Divider(), + const Padding( + padding: EdgeInsets.all(20), + child: Text( + "发电小功能 \n" + " 多线程下载\n" + " 批量导入导出\n" + " 跳页", + ), + ), + const Divider(), + const Divider(), ], ), ); } - void managementPat() { - // todo + List patPro() { + List widgets = []; + if (proInfoPat.accessKey.isNotEmpty) { + var text = "密钥 : 已录入"; + if (proInfoPat.patId.isNotEmpty) { + text += "\nPAT账号 : ${proInfoPat.patId}"; + } + if (proInfoPat.bindUid.isNotEmpty) { + text += "\n绑定PIKA账号 : ${proInfoPat.bindUid}"; + } + if (proInfoPat.requestDelete > 0) { + DateTime dateTime = DateTime.fromMillisecondsSinceEpoch( + proInfoPat.requestDelete * 1000, + isUtc: true, + ); + String formattedDate = + DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime.toLocal()); + text += "\n绑定账号时间 : $formattedDate"; + } + if (proInfoPat.reBind > 0) { + DateTime dateTime = DateTime.fromMillisecondsSinceEpoch( + proInfoPat.reBind * 1000, + isUtc: true, + ); + String formattedDate = + DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime.toLocal()); + text += "\n可以换绑时间 : $formattedDate"; + } + List append = []; + if (proInfoPat.bindUid == "") { + append.add(const TextSpan( + text: "\n(请点击这里绑定到当前账号发电)", + style: TextStyle(color: Colors.blue), + )); + } else if (proInfoPat.bindUid != _username) { + append.add(const TextSpan( + text: "\n(请点换绑到当前账号发电)", + style: TextStyle(color: Colors.red), + )); + } else if (proInfoPat.isPro == false) { + append.add(const TextSpan( + text: "\n(未检测到入会, 请到下载页入会)", + style: TextStyle(color: Colors.orange), + )); + } else { + append.add(const TextSpan( + text: "\n(PAT正常)", + style: TextStyle(color: Colors.green), + )); + } + widgets.add(ListTile( + onTap: () async { + print(jsonEncode(proInfoPat)); + var choose = await chooseMapDialog( + context, + { + "更新PAT发电状态": 2, + "绑定到此账号": 3, + "更换PAT密钥": 1, + "清除PAT信息": 4, + }, + "请选择", + ); + switch (choose) { + case 1: + addPatAccount(); + break; + case 2: + reloadPatAccount(); + break; + case 3: + bindThisAccount(); + break; + case 4: + clearPat(); + break; + } + }, + title: const Text("PAT入会"), + subtitle: Text.rich(TextSpan(children: [ + TextSpan(text: text), + ...append, + ])), + )); + } else { + widgets.add(ListTile( + onTap: () { + addPatAccount(); + }, + title: const Text("PAT入会"), + subtitle: const Text("点击绑定"), + )); + } + return widgets; + } + + void addPatAccount() async { + print(jsonEncode(proInfoPat)); + String? key = await inputString(context, "请输入授权代码"); + if (key != null) { + await Navigator.of(context) + .push(mixRoute(builder: (BuildContext context) { + return AccessKeyReplaceScreen(accessKey: key); + })); + } + } + + reloadPatAccount() async { + defaultToast(context, "请稍后"); + try { + await method.reloadPatAccount(); + await reloadIsPro(); + defaultToast(context, "SUCCESS"); + } catch (e) { + defaultToast(context, "FAIL : $e"); + } finally {} + } + + bindThisAccount() async { + defaultToast(context, "请稍后"); + try { + await method.bindThisAccount(); + await reloadIsPro(); + defaultToast(context, "SUCCESS"); + } catch (e) { + defaultToast(context, "FAIL : $e"); + } finally {} + } + + clearPat() async { + await method.clearPat(); + await reloadIsPro(); + defaultToast(context, "Success"); } } diff --git a/lib/screens/SearchScreen.dart b/lib/screens/SearchScreen.dart index b7433c1..4015cf2 100644 --- a/lib/screens/SearchScreen.dart +++ b/lib/screens/SearchScreen.dart @@ -8,8 +8,10 @@ import 'package:pikapika/screens/components/RightClickPop.dart'; import '../basic/Entities.dart'; import '../basic/config/Address.dart'; import '../basic/config/IconLoading.dart'; +import 'components/ComicList.dart'; import 'components/ComicPager.dart'; import 'components/Common.dart'; +import 'components/GoDownloadSelect.dart'; // 搜索页面 class SearchScreen extends StatefulWidget { @@ -27,6 +29,7 @@ class SearchScreen extends StatefulWidget { } class _SearchScreenState extends State { + late final _comicListController = ComicListController(); late final TextEditingController _textEditController = TextEditingController(text: widget.keyword); late final SearchBar _searchBar = SearchBar( @@ -51,7 +54,11 @@ class _SearchScreenState extends State { return AppBar( title: Text("${categoryTitle(widget.category)} ${widget.keyword}"), actions: [ - commonPopMenu(context), + commonPopMenu( + context, + setState: setState, + comicListController: _comicListController, + ), addressPopMenu(context), _chooseCategoryAction(), _searchBar.getSearchAction(context), @@ -66,7 +73,7 @@ class _SearchScreenState extends State { categoryTitle(null), ...filteredList( storedCategories, - (c) => !shadowCategories.contains(c), + (c) => !shadowCategories.contains(c), ), ]); if (category != null) { @@ -100,7 +107,7 @@ class _SearchScreenState extends State { } @override - Widget build(BuildContext context){ + Widget build(BuildContext context) { return rightClickPop( child: buildScreen(context), context: context, @@ -110,7 +117,9 @@ class _SearchScreenState extends State { Widget buildScreen(BuildContext context) { return Scaffold( - appBar: _searchBar.build(context), + appBar: _comicListController.selecting + ? downAppBar(context, _comicListController, setState) + : _searchBar.build(context), body: ComicPager( fetchPage: _fetch, ), diff --git a/lib/screens/components/Common.dart b/lib/screens/components/Common.dart index b4b926f..0d2b220 100644 --- a/lib/screens/components/Common.dart +++ b/lib/screens/components/Common.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:pikapika/basic/Common.dart'; import 'package:pikapika/screens/components/ComicList.dart'; import '../../basic/config/IsPro.dart'; @@ -39,9 +40,12 @@ Widget commonPopMenu( PopupMenuItem( value: 3, child: ListTile( - leading: const Icon(Icons.download), + leading: Icon( + Icons.download, + color: isPro ? null : Colors.grey, + ), title: Text( - "下载" + (isPro ? "" : "Pro"), + "批量下载" + (isPro ? "" : "(发电)"), style: TextStyle( color: isPro ? null : Colors.grey, ), @@ -63,6 +67,10 @@ Widget commonPopMenu( chooseShadowCategories(context); break; case 3: + if (!isPro) { + defaultToast(context, "请先发电呀"); + return; + } if (setState != null) { if (comicListController != null) { setState(() { diff --git a/lib/screens/components/GoDownloadSelect.dart b/lib/screens/components/GoDownloadSelect.dart index dd3cbbd..986136d 100644 --- a/lib/screens/components/GoDownloadSelect.dart +++ b/lib/screens/components/GoDownloadSelect.dart @@ -58,13 +58,15 @@ AppBar downAppBar( MaterialButton( minWidth: 0, onPressed: () async { - // todo - final list = _comicListController.selected; + var list = _comicListController.selected; if (list.isEmpty) { defaultToast(context, "请选择漫画"); return; } - _comicListController.selecting = false; + list = list.toList(); + setState((){ + _comicListController.selecting = false; + }); Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { return DownloadComicsScreen(list); diff --git a/pubspec.lock b/pubspec.lock index e1add07..318e637 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: archive - sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" url: "https://pub.dev" source: hosted - version: "3.3.6" + version: "3.3.7" async: dependency: transitive description: @@ -273,10 +273,10 @@ packages: dependency: transitive description: name: image_picker_ios - sha256: d4cb8ab04f770dab9d04c7959e5f6d22e8c5280343d425f9344f93832cf58445 + sha256: a1546ff5861fc15812953d4733b520c3d371cec3d2859a001ff04c46c4d81883 url: "https://pub.dev" source: hosted - version: "0.8.7+2" + version: "0.8.7+3" image_picker_platform_interface: dependency: transitive description: @@ -286,7 +286,7 @@ packages: source: hosted version: "2.6.3" intl: - dependency: transitive + dependency: "direct main" description: name: intl sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" @@ -574,10 +574,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92" + sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" url: "https://pub.dev" source: hosted - version: "6.1.3" + version: "6.1.4" url_launcher_linux: dependency: transitive description: @@ -630,10 +630,10 @@ packages: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 112486b..b43e4b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: uri_to_file: ^0.2.0 uni_links: ^0.5.1 filesystem_picker: ^3.0.0-beta.1 + intl: ^0.17.0 dev_dependencies: