v1.5.5
This commit is contained in:
parent
ef4ce20587
commit
d02510c3cf
|
@ -4,7 +4,7 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
go_version: '1.17'
|
||||
go_version: '1.18'
|
||||
flutter_channel: 'stable'
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ if (flutterRoot == null) {
|
|||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
flutterVersionCode = '2'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
flutterVersionName = '1.0.1'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
|
|
@ -1 +1 @@
|
|||
v1.5.4
|
||||
v1.5.5
|
|
@ -1,11 +1,13 @@
|
|||
更新
|
||||
|
||||
- [x] 苹果手机支持导出导出了
|
||||
- [x] 修复了安卓导入时不能选择文件 (AOSP个别机型的问题)
|
||||
- [x] 增加了导出 PKI/PKZ 等格式
|
||||
- [x] 苹果/安卓 在文件管理器中可以直接打开 PKI/PKZ 导入或观看
|
||||
- [x] 增加了骑士榜
|
||||
- [x] 对历史记录页面进行优化
|
||||
- [x] 对导入进行优化
|
||||
- [x] 增加了批量导出ZIP/PKI到文件夹
|
||||
- [x] 增加了从一个文件夹中导入所有ZIP/PKI的功能
|
||||
- [x] 增加了发电页面, 对作者发过电的用户会展示发电特权图标
|
||||
|
||||
计划
|
||||
|
||||
- [ ] 本地骑士书签 (漫画书签?/快捷搜索书签?)
|
||||
- [ ] 将Jasmine导出的PKI/JMI/导入, 阅读Jasmine导出的PKI
|
||||
|
||||
|
|
|
@ -359,7 +359,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
|
@ -368,10 +368,12 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = niuhuan.pikapika;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
|
@ -491,7 +493,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
|
@ -500,11 +502,13 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = niuhuan.pikapika;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -515,7 +519,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
|
@ -524,10 +528,12 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = niuhuan.pikapika;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>pika</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
|
@ -31,36 +22,6 @@
|
|||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>niuhuan.pkz</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.content</string>
|
||||
<string>com.apple.package</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>PKZ Archive</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>pkz</string>
|
||||
<string>pki</string>
|
||||
<string>zip</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/vnd.niuhuan.pkz</string>
|
||||
<string>text/vnd.niuhuan.pki</string>
|
||||
<string>text/vnd.niuhuan.zip</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
@ -72,11 +33,20 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>pika</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.entertainment</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
@ -104,5 +74,35 @@
|
|||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.content</string>
|
||||
<string>com.apple.package</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>PKZ Archive</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>niuhuan.pkz</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>pkz</string>
|
||||
<string>pki</string>
|
||||
<string>zip</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/vnd.niuhuan.pkz</string>
|
||||
<string>text/vnd.niuhuan.pki</string>
|
||||
<string>text/vnd.niuhuan.zip</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1012,3 +1012,13 @@ class PkzComicViewLog {
|
|||
return _data;
|
||||
}
|
||||
}
|
||||
|
||||
class IsPro {
|
||||
late bool isPro;
|
||||
late int expire;
|
||||
|
||||
IsPro.fromJson(Map<String, dynamic> json) {
|
||||
this.isPro = json["isPro"];
|
||||
this.expire = json["expire"];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -548,7 +548,8 @@ class Method {
|
|||
}
|
||||
|
||||
/// 导出下载的漫画到pki
|
||||
Future<dynamic> exportComicDownloadToPki(String comicId, String dir, String name) {
|
||||
Future<dynamic> exportComicDownloadToPki(
|
||||
String comicId, String dir, String name) {
|
||||
return _flatInvoke("exportComicDownloadToPki", {
|
||||
"comicId": comicId,
|
||||
"dir": dir,
|
||||
|
@ -582,6 +583,35 @@ class Method {
|
|||
});
|
||||
}
|
||||
|
||||
/// 导出zip
|
||||
Future<dynamic> exportAnyComicDownloadsToZip(
|
||||
List<String> comicIds,
|
||||
String dir,
|
||||
) {
|
||||
return _flatInvoke("exportAnyComicDownloadsToZip", {
|
||||
"comicIds": comicIds,
|
||||
"dir": dir,
|
||||
});
|
||||
}
|
||||
|
||||
/// 导出pki
|
||||
Future<dynamic> exportAnyComicDownloadsToPki(
|
||||
List<String> comicIds,
|
||||
String dir,
|
||||
) {
|
||||
return _flatInvoke("exportAnyComicDownloadsToPki", {
|
||||
"comicIds": comicIds,
|
||||
"dir": dir,
|
||||
});
|
||||
}
|
||||
|
||||
/// 导入文件夹所有的文件
|
||||
Future<dynamic> importComicDownloadDir(
|
||||
String dir,
|
||||
) {
|
||||
return _flatInvoke("importComicDownloadDir", dir);
|
||||
}
|
||||
|
||||
/// 使用网络将下载传输到其他设备
|
||||
Future<int> exportComicUsingSocket(String comicId) async {
|
||||
return int.parse(await _flatInvoke("exportComicUsingSocket", comicId));
|
||||
|
@ -833,4 +863,16 @@ class Method {
|
|||
.map((e) => Knight.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<IsPro> isPro() async {
|
||||
return IsPro.fromJson(jsonDecode(await _flatInvoke("isPro", "")));
|
||||
}
|
||||
|
||||
Future reloadPro() {
|
||||
return _flatInvoke("reloadPro", "");
|
||||
}
|
||||
|
||||
Future inputCdKey(String cdKey) {
|
||||
return _flatInvoke("inputCdKey", cdKey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:pikapika/basic/Method.dart';
|
||||
|
||||
import '../Common.dart';
|
||||
import 'IsPro.dart';
|
||||
|
||||
const _propertyName = "androidDisplayMode";
|
||||
List<String> _modes = [];
|
||||
|
@ -27,7 +28,11 @@ Future<void> _chooseAndroidDisplayMode(BuildContext context) async {
|
|||
if (Platform.isAndroid) {
|
||||
List<String> list = [""];
|
||||
list.addAll(_modes);
|
||||
String? result = await chooseListDialog<String>(context, "安卓屏幕刷新率", list);
|
||||
String? result = await chooseListDialog<String>(
|
||||
context,
|
||||
"安卓屏幕刷新率 \n(省电模式下不会高刷)",
|
||||
list,
|
||||
);
|
||||
if (result != null) {
|
||||
await method.saveProperty(_propertyName, result);
|
||||
_androidDisplayMode = result;
|
||||
|
@ -41,9 +46,18 @@ Widget androidDisplayModeSetting() {
|
|||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||
return ListTile(
|
||||
title: const Text("屏幕刷新率(安卓)"),
|
||||
title: Text(
|
||||
"屏幕刷新率(安卓)" + (!isPro ? "(发电)" : ""),
|
||||
style: TextStyle(
|
||||
color: !isPro ? Colors.grey : null,
|
||||
),
|
||||
),
|
||||
subtitle: Text(_androidDisplayMode),
|
||||
onTap: () async {
|
||||
if (!isPro) {
|
||||
defaultToast(context, "请先发电再使用");
|
||||
return;
|
||||
}
|
||||
await _chooseAndroidDisplayMode(context);
|
||||
setState(() {});
|
||||
},
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import '../Common.dart';
|
||||
import '../Method.dart';
|
||||
import 'IsPro.dart';
|
||||
|
||||
const _propertyName = "androidSecureFlag";
|
||||
|
||||
|
@ -37,9 +38,20 @@ Widget androidSecureFlagSetting() {
|
|||
return StatefulBuilder(builder:
|
||||
(BuildContext context, void Function(void Function()) setState) {
|
||||
return ListTile(
|
||||
title: const Text("禁止截图/禁止显示在任务视图"),
|
||||
subtitle: Text(_androidSecureFlag ? "是" : "否"),
|
||||
title: Text(
|
||||
"禁止截图/禁止显示在任务视图" + (!isPro ? "(发电)" : ""),
|
||||
style: TextStyle(
|
||||
color: !isPro ? Colors.grey : null,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
_androidSecureFlag ? "是" : "否",
|
||||
),
|
||||
onTap: () async {
|
||||
if (!isPro) {
|
||||
defaultToast(context, "请先发电再使用");
|
||||
return;
|
||||
}
|
||||
await _chooseAndroidSecureFlag(context);
|
||||
setState(() {});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:pikapika/basic/Common.dart';
|
||||
import 'package:pikapika/basic/Method.dart';
|
||||
|
||||
import 'IsPro.dart';
|
||||
|
||||
late int _downloadThreadCount;
|
||||
const _values = [1, 2, 3, 4, 5];
|
||||
|
||||
|
@ -15,9 +17,18 @@ Widget downloadThreadCountSetting() {
|
|||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||
return ListTile(
|
||||
title: const Text("下载线程数"),
|
||||
title: Text(
|
||||
"下载线程数" + (!isPro ? "(发电)" : ""),
|
||||
style: TextStyle(
|
||||
color: !isPro ? Colors.grey : null,
|
||||
),
|
||||
),
|
||||
subtitle: Text("$_downloadThreadCount"),
|
||||
onTap: () async {
|
||||
if (!isPro) {
|
||||
defaultToast(context, "请先发电再使用");
|
||||
return;
|
||||
}
|
||||
int? value = await chooseListDialog(context, "选择下载线程数", _values);
|
||||
if (value != null) {
|
||||
await method.saveDownloadThreadCount(value);
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import '../Common.dart';
|
||||
import '../Method.dart';
|
||||
import 'IsPro.dart';
|
||||
|
||||
const _propertyName = "exportRename";
|
||||
late bool _exportRename;
|
||||
|
@ -29,9 +30,18 @@ Widget exportRenameSetting() {
|
|||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||
return ListTile(
|
||||
title: const Text("导出时进行重命名"),
|
||||
title: Text(
|
||||
"导出时进行重命名" + (!isPro ? "(发电)" : ""),
|
||||
style: TextStyle(
|
||||
color: !isPro ? Colors.grey : null,
|
||||
),
|
||||
),
|
||||
subtitle: Text(_exportRename ? "是" : "否"),
|
||||
onTap: () async {
|
||||
if (!isPro) {
|
||||
defaultToast(context, "请先发电再使用");
|
||||
return;
|
||||
}
|
||||
await _chooseExportRename(context);
|
||||
setState(() {});
|
||||
},
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:event/event.dart';
|
||||
import 'package:pikapika/basic/Method.dart';
|
||||
|
||||
var isPro = false;
|
||||
var isProEx = 0;
|
||||
|
||||
final proEvent = Event();
|
||||
|
||||
Future reloadIsPro() async {
|
||||
final p = await method.isPro();
|
||||
isPro = p.isPro;
|
||||
isProEx = p.expire;
|
||||
proEvent.broadcast();
|
||||
}
|
|
@ -15,9 +15,6 @@ late String _version;
|
|||
String? _latestVersion;
|
||||
String? _latestVersionInfo;
|
||||
|
||||
const _propertyName = "checkVersionPeriod";
|
||||
late int _period = -1;
|
||||
|
||||
Future initVersion() async {
|
||||
// 当前版本
|
||||
try {
|
||||
|
@ -25,14 +22,6 @@ Future initVersion() async {
|
|||
} catch (e) {
|
||||
_version = "dirty";
|
||||
}
|
||||
// 检查周期
|
||||
_period = int.parse(await method.loadProperty(_propertyName, "0"));
|
||||
if (_period > 0) {
|
||||
if (DateTime.now().millisecondsSinceEpoch > _period) {
|
||||
await method.saveProperty(_propertyName, "0");
|
||||
_period = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var versionEvent = Event<EventArgs>();
|
||||
|
@ -50,10 +39,6 @@ String? latestVersionInfo() {
|
|||
}
|
||||
|
||||
Future autoCheckNewVersion() {
|
||||
if (_period != 0) {
|
||||
// -1 不检查, >0 未到检查时间
|
||||
return Future.value();
|
||||
}
|
||||
return _versionCheck();
|
||||
}
|
||||
|
||||
|
@ -86,75 +71,3 @@ Future _versionCheck() async {
|
|||
} // else dirtyVersion
|
||||
versionEvent.broadcast();
|
||||
}
|
||||
|
||||
String _periodText() {
|
||||
if (_period < 0) {
|
||||
return "自动检查更新已关闭";
|
||||
}
|
||||
if (_period == 0) {
|
||||
return "自动检查更新已开启";
|
||||
}
|
||||
return "下次检查时间 : " +
|
||||
formatDateTimeToDateTime(
|
||||
DateTime.fromMillisecondsSinceEpoch(_period),
|
||||
);
|
||||
}
|
||||
|
||||
Future _choosePeriod(BuildContext context) async {
|
||||
var result = await chooseListDialog(
|
||||
context,
|
||||
"自动检查更新",
|
||||
["开启", "一周后", "一个月后", "一年后", "关闭"],
|
||||
tips: "重启后红点会消失",
|
||||
);
|
||||
switch (result) {
|
||||
case "开启":
|
||||
await method.saveProperty(_propertyName, "0");
|
||||
_period = 0;
|
||||
break;
|
||||
case "一周后":
|
||||
var time = DateTime.now().millisecondsSinceEpoch + (1000 * 3600 * 24 * 7);
|
||||
await method.saveProperty(_propertyName, "$time");
|
||||
_period = time;
|
||||
break;
|
||||
case "一个月后":
|
||||
var time =
|
||||
DateTime.now().millisecondsSinceEpoch + (1000 * 3600 * 24 * 30);
|
||||
await method.saveProperty(_propertyName, "$time");
|
||||
_period = time;
|
||||
break;
|
||||
case "一年后":
|
||||
var time =
|
||||
DateTime.now().millisecondsSinceEpoch + (1000 * 3600 * 24 * 365);
|
||||
await method.saveProperty(_propertyName, "$time");
|
||||
_period = time;
|
||||
break;
|
||||
case "关闭":
|
||||
await method.saveProperty(_propertyName, "-1");
|
||||
_period = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Widget autoUpdateCheckSetting() {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||
return ListTile(
|
||||
title: const Text("自动检查更新"),
|
||||
subtitle: Text(_periodText()),
|
||||
onTap: () async {
|
||||
await _choosePeriod(context);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String formatDateTimeToDateTime(DateTime c) {
|
||||
try {
|
||||
return "${add0(c.year, 4)}-${add0(c.month, 2)}-${add0(c.day, 2)} ${add0(c.hour, 2)}:${add0(c.minute, 2)}";
|
||||
} catch (e) {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,8 +98,6 @@ class _AboutScreenState extends State<AboutScreen> {
|
|||
),
|
||||
),
|
||||
const Divider(),
|
||||
autoUpdateCheckSetting(),
|
||||
const Divider(),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: const SelectableText(
|
||||
|
|
|
@ -5,18 +5,15 @@ import 'package:flutter/gestures.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:pikapika/basic/Common.dart';
|
||||
import 'package:pikapika/basic/Method.dart';
|
||||
import 'package:pikapika/basic/config/IsPro.dart';
|
||||
import 'package:pikapika/basic/config/Themes.dart';
|
||||
import 'package:pikapika/basic/enum/ErrorTypes.dart';
|
||||
import 'package:pikapika/screens/RegisterScreen.dart';
|
||||
import 'package:pikapika/screens/SettingsScreen.dart';
|
||||
import 'package:pikapika/screens/components/NetworkSetting.dart';
|
||||
import 'package:uni_links/uni_links.dart';
|
||||
import 'package:uri_to_file/uri_to_file.dart';
|
||||
|
||||
import '../basic/Navigator.dart';
|
||||
import 'AppScreen.dart';
|
||||
import 'DownloadListScreen.dart';
|
||||
import 'PkzArchiveScreen.dart';
|
||||
import 'ThemeScreen.dart';
|
||||
import 'components/ContentLoading.dart';
|
||||
|
||||
|
@ -186,6 +183,7 @@ class _AccountScreenState extends State<AccountScreen> {
|
|||
});
|
||||
try {
|
||||
await method.login();
|
||||
await reloadIsPro();
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const AppScreen()),
|
||||
|
|
|
@ -87,6 +87,7 @@ class _DownloadConfirmScreenState extends State<DownloadConfirmScreen> {
|
|||
});
|
||||
}
|
||||
}
|
||||
try {
|
||||
// 如果之前下载过就将EP加入下载
|
||||
// 如果之前没有下载过就创建下载
|
||||
if (_task != null) {
|
||||
|
@ -97,10 +98,13 @@ class _DownloadConfirmScreenState extends State<DownloadConfirmScreen> {
|
|||
// 退出
|
||||
defaultToast(context, "已经加入下载列表");
|
||||
Navigator.pop(context);
|
||||
} catch (e, s) {
|
||||
defaultToast(context, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context){
|
||||
Widget build(BuildContext context) {
|
||||
return rightClickPop(
|
||||
child: buildScreen(context),
|
||||
context: context,
|
||||
|
|
|
@ -5,6 +5,7 @@ import '../basic/Channels.dart';
|
|||
import '../basic/Cross.dart';
|
||||
import '../basic/Method.dart';
|
||||
import '../basic/config/ExportRename.dart';
|
||||
import '../basic/config/IsPro.dart';
|
||||
import 'components/ContentLoading.dart';
|
||||
|
||||
class DownloadExportingGroupScreen extends StatefulWidget {
|
||||
|
@ -51,17 +52,31 @@ class _DownloadExportingGroupScreenState
|
|||
return Center(child: Text("导出失败\n$e"));
|
||||
}
|
||||
if (exported) {
|
||||
return Center(child: Text("导出成功"));
|
||||
return const Center(child: Text("导出成功"));
|
||||
}
|
||||
return Center(
|
||||
child: MaterialButton(
|
||||
onPressed: _export,
|
||||
child: const Text("选择导出位置"),
|
||||
return ListView(
|
||||
children: [
|
||||
Container(height: 20),
|
||||
MaterialButton(
|
||||
onPressed: _exportPkz,
|
||||
child: const Text("导出PKZ"),
|
||||
),
|
||||
Container(height: 20),
|
||||
MaterialButton(
|
||||
onPressed: _exportPkis,
|
||||
child: Text("分别导出PKI" + (!isPro ? "\n(发电后使用)" : "")),
|
||||
),
|
||||
Container(height: 20),
|
||||
MaterialButton(
|
||||
onPressed: _exportZips,
|
||||
child: Text("分别导出ZIP" + (!isPro ? "\n(发电后使用)" : "")),
|
||||
),
|
||||
Container(height: 20),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_export() async {
|
||||
_exportPkz() async {
|
||||
late String? path;
|
||||
try {
|
||||
path = await chooseFolder(context);
|
||||
|
@ -105,6 +120,74 @@ class _DownloadExportingGroupScreenState
|
|||
}
|
||||
}
|
||||
|
||||
_exportPkis() async {
|
||||
if (!isPro) {
|
||||
defaultToast(context, "请先发电鸭");
|
||||
return;
|
||||
}
|
||||
late String? path;
|
||||
try {
|
||||
path = await chooseFolder(context);
|
||||
} catch (e) {
|
||||
defaultToast(context, "$e");
|
||||
return;
|
||||
}
|
||||
print("path $path");
|
||||
if (path != null) {
|
||||
try {
|
||||
setState(() {
|
||||
exporting = true;
|
||||
});
|
||||
await method.exportAnyComicDownloadsToPki(
|
||||
widget.idList,
|
||||
path,
|
||||
);
|
||||
exported = true;
|
||||
} catch (err) {
|
||||
e = err;
|
||||
exportFail = true;
|
||||
} finally {
|
||||
setState(() {
|
||||
exporting = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_exportZips() async {
|
||||
if (!isPro) {
|
||||
defaultToast(context, "请先发电鸭");
|
||||
return;
|
||||
}
|
||||
late String? path;
|
||||
try {
|
||||
path = await chooseFolder(context);
|
||||
} catch (e) {
|
||||
defaultToast(context, "$e");
|
||||
return;
|
||||
}
|
||||
print("path $path");
|
||||
if (path != null) {
|
||||
try {
|
||||
setState(() {
|
||||
exporting = true;
|
||||
});
|
||||
await method.exportAnyComicDownloadsToZip(
|
||||
widget.idList,
|
||||
path,
|
||||
);
|
||||
exported = true;
|
||||
} catch (err) {
|
||||
e = err;
|
||||
exportFail = true;
|
||||
} finally {
|
||||
setState(() {
|
||||
exporting = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
|
|
|
@ -8,6 +8,8 @@ import 'package:pikapika/basic/Common.dart';
|
|||
import 'package:pikapika/basic/Method.dart';
|
||||
import 'package:pikapika/basic/config/ChooserRoot.dart';
|
||||
|
||||
import '../basic/Cross.dart';
|
||||
import '../basic/config/IsPro.dart';
|
||||
import 'PkzArchiveScreen.dart';
|
||||
import 'components/ContentLoading.dart';
|
||||
import 'components/RightClickPop.dart';
|
||||
|
@ -64,6 +66,7 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
|
|||
|
||||
actions.add(_fileImportButton());
|
||||
actions.add(_networkImportButton());
|
||||
actions.add(_importDirFilesZipButton());
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
@ -103,7 +106,7 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
|
|||
allowedExtensions: ['.pkz', '.zip', '.pki'],
|
||||
fileTileSelectMode: FileTileSelectMode.wholeTile,
|
||||
);
|
||||
}else{
|
||||
} else {
|
||||
var ls = await FilePicker.platform.pickFiles(
|
||||
dialogTitle: '选择要导入的文件',
|
||||
allowMultiple: false,
|
||||
|
@ -127,9 +130,9 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
|
|||
setState(() {
|
||||
_importing = true;
|
||||
});
|
||||
if(path.endsWith(".zip")){
|
||||
if (path.endsWith(".zip")) {
|
||||
await method.importComicDownload(path);
|
||||
} else if(path.endsWith(".pki")){
|
||||
} else if (path.endsWith(".pki")) {
|
||||
await method.importComicDownloadPki(path);
|
||||
}
|
||||
setState(() {
|
||||
|
@ -184,4 +187,43 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
|
|||
child: const Text('从其他设备导入'),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _importDirFilesZipButton() {
|
||||
return MaterialButton(
|
||||
height: 80,
|
||||
onPressed: () async {
|
||||
late String? path;
|
||||
try {
|
||||
path = await chooseFolder(context);
|
||||
} catch (e) {
|
||||
defaultToast(context, "$e");
|
||||
return;
|
||||
}
|
||||
if (path != null) {
|
||||
try {
|
||||
setState(() {
|
||||
_importing = true;
|
||||
});
|
||||
await method.importComicDownloadDir(path);
|
||||
setState(() {
|
||||
_importMessage = "导入成功";
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_importMessage = "导入失败 $e";
|
||||
});
|
||||
} finally {
|
||||
setState(() {
|
||||
_importing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
'选择文件夹\n(导入里面所有的zip)' + (!isPro ? "\n(发电后使用)" : ""),
|
||||
style: TextStyle(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import 'package:pikapika/screens/PkzArchiveScreen.dart';
|
|||
import 'package:uni_links/uni_links.dart';
|
||||
import 'package:uri_to_file/uri_to_file.dart';
|
||||
import '../basic/config/ExportRename.dart';
|
||||
import '../basic/config/IsPro.dart';
|
||||
import 'AccountScreen.dart';
|
||||
import 'AppScreen.dart';
|
||||
import 'DownloadOnlyImportScreen.dart';
|
||||
|
@ -94,6 +95,7 @@ class _InitScreenState extends State<InitScreen> {
|
|||
await initVersion();
|
||||
await initUsingRightClickPop();
|
||||
await initAuthentication();
|
||||
await reloadIsPro();
|
||||
autoCheckNewVersion();
|
||||
|
||||
String? initUrl;
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:pikapika/basic/Common.dart';
|
||||
import 'package:pikapika/basic/Method.dart';
|
||||
|
||||
import '../basic/config/IsPro.dart';
|
||||
|
||||
class ProScreen extends StatefulWidget {
|
||||
const ProScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ProScreenState();
|
||||
}
|
||||
|
||||
class _ProScreenState extends State<ProScreen> {
|
||||
String _username = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
method.getUsername().then((value) {
|
||||
setState(() {
|
||||
_username = value;
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var size = MediaQuery.of(context).size;
|
||||
var min = size.width < size.height ? size.width : size.height;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("发电中心"),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: min / 2,
|
||||
height: min / 2,
|
||||
child: Center(
|
||||
child: Icon(
|
||||
isPro ? Icons.offline_bolt : Icons.offline_bolt_outlined,
|
||||
size: min / 3,
|
||||
color: Colors.grey.shade500,
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(child: Text(_username)),
|
||||
Container(height: 20),
|
||||
const Divider(),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Text(
|
||||
"点击\"我曾经发过电\"进同步发电状态\n"
|
||||
"点击\"我刚才发了电\"兑换作者给您的礼物卡\n"
|
||||
"去\"关于\"界面找到维护地址用爱发电",
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: const Text("发电详情"),
|
||||
subtitle: Text(
|
||||
isPro
|
||||
? "发电中 (${DateTime.fromMillisecondsSinceEpoch(1000 * isProEx).toString()})"
|
||||
: "未发电",
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: const Text("我曾经发过电"),
|
||||
onTap: () async {
|
||||
try {
|
||||
await method.reloadPro();
|
||||
defaultToast(context, "SUCCESS");
|
||||
} catch (e, s) {
|
||||
defaultToast(context, "FAIL");
|
||||
}
|
||||
await reloadIsPro();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: const Text("我刚才发了电"),
|
||||
onTap: () async {
|
||||
final code = await inputString(context, "输入代码");
|
||||
if (code != null && code.isNotEmpty) {
|
||||
try {
|
||||
await method.inputCdKey(code);
|
||||
defaultToast(context, "SUCCESS");
|
||||
} catch (e, s) {
|
||||
defaultToast(context, "FAIL");
|
||||
}
|
||||
}
|
||||
await reloadIsPro();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import 'package:pikapika/basic/config/DownloadThreadCount.dart';
|
|||
import 'package:pikapika/basic/config/ExportRename.dart';
|
||||
import 'package:pikapika/basic/config/FullScreenAction.dart';
|
||||
import 'package:pikapika/basic/config/FullScreenUI.dart';
|
||||
import 'package:pikapika/basic/config/IsPro.dart';
|
||||
import 'package:pikapika/basic/config/KeyboardController.dart';
|
||||
import 'package:pikapika/basic/config/NoAnimation.dart';
|
||||
import 'package:pikapika/basic/config/PagerAction.dart';
|
||||
|
@ -113,8 +114,6 @@ class SettingsScreen extends StatelessWidget {
|
|||
const Divider(),
|
||||
migrate(context),
|
||||
const Divider(),
|
||||
autoUpdateCheckSetting(),
|
||||
const Divider(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -122,9 +121,18 @@ class SettingsScreen extends StatelessWidget {
|
|||
Widget migrate(BuildContext context) {
|
||||
if (Platform.isAndroid) {
|
||||
return ListTile(
|
||||
title: const Text("文件迁移"),
|
||||
subtitle: const Text("更换您的数据文件夹"),
|
||||
title: Text(
|
||||
"文件迁移" + (!isPro ? "(发电)" : ""),
|
||||
style: TextStyle(
|
||||
color: !isPro ? Colors.grey : null,
|
||||
),
|
||||
),
|
||||
subtitle: const Text("更换您的数据文件夹到内存卡"),
|
||||
onTap: () async {
|
||||
if (!isPro) {
|
||||
defaultToast(context, "请先发电再使用");
|
||||
return;
|
||||
}
|
||||
var f =
|
||||
await confirmDialog(context, "文件迁移", "此功能菜单保存后, 需要重启程序, 您确认吗");
|
||||
if (f) {
|
||||
|
|
|
@ -5,10 +5,12 @@ import 'package:pikapika/screens/AboutScreen.dart';
|
|||
import 'package:pikapika/screens/AccountScreen.dart';
|
||||
import 'package:pikapika/screens/DownloadListScreen.dart';
|
||||
import 'package:pikapika/screens/FavouritePaperScreen.dart';
|
||||
import 'package:pikapika/screens/ProScreen.dart';
|
||||
import 'package:pikapika/screens/ThemeScreen.dart';
|
||||
import 'package:pikapika/screens/ViewLogsScreen.dart';
|
||||
import 'package:pikapika/basic/Method.dart';
|
||||
|
||||
import '../basic/config/IsPro.dart';
|
||||
import '../basic/config/Themes.dart';
|
||||
import 'SettingsScreen.dart';
|
||||
import 'components/Badge.dart';
|
||||
|
@ -25,17 +27,19 @@ class SpaceScreen extends StatefulWidget {
|
|||
class _SpaceScreenState extends State<SpaceScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
versionEvent.subscribe(_onVersion);
|
||||
versionEvent.subscribe(_onEvent);
|
||||
proEvent.subscribe(_onEvent);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
versionEvent.unsubscribe(_onVersion);
|
||||
versionEvent.unsubscribe(_onEvent);
|
||||
proEvent.unsubscribe(_onEvent);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onVersion(dynamic a) {
|
||||
void _onEvent(dynamic a) {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
@ -73,6 +77,17 @@ class _SpaceScreenState extends State<SpaceScreen> {
|
|||
badge: latestVersion() == null ? null : "1",
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (BuildContext context) {
|
||||
return const ProScreen();
|
||||
}));
|
||||
},
|
||||
icon: Icon(
|
||||
isPro ? Icons.offline_bolt : Icons.offline_bolt_outlined,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pikapika/basic/Common.dart';
|
||||
import 'package:pikapika/basic/Method.dart';
|
||||
import 'package:pikapika/screens/components/ComicInfoCard.dart';
|
||||
import 'package:pikapika/screens/components/RightClickPop.dart';
|
||||
|
||||
import '../basic/Entities.dart';
|
||||
import 'ComicInfoScreen.dart';
|
||||
import 'components/Images.dart';
|
||||
|
||||
|
@ -19,7 +23,7 @@ class _ViewLogsScreenState extends State<ViewLogsScreen> {
|
|||
static const _scrollPhysics = AlwaysScrollableScrollPhysics(); // 即使不足一页仍可滚动
|
||||
|
||||
final _scrollController = ScrollController();
|
||||
final _comicList = <ViewLogWrapEntity>[];
|
||||
final _comicList = <ViewLog>[];
|
||||
|
||||
var _isLoading = false; // 是否加载中
|
||||
var _scrollOvered = false; // 滚动到最后
|
||||
|
@ -70,8 +74,7 @@ class _ViewLogsScreenState extends State<ViewLogsScreen> {
|
|||
if (page.isEmpty) {
|
||||
_scrollOvered = true;
|
||||
} else {
|
||||
_comicList.addAll(page.map((e) =>
|
||||
ViewLogWrapEntity(e.id, e.title, e.thumbFileServer, e.thumbPath)));
|
||||
_comicList.addAll(page);
|
||||
}
|
||||
_offset += _pageSize;
|
||||
} finally {
|
||||
|
@ -106,25 +109,37 @@ class _ViewLogsScreenState extends State<ViewLogsScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var entries = _comicList.map((e) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
_chooseComic(e.id);
|
||||
},
|
||||
onLongPress: () {
|
||||
_clearOnce(e.id);
|
||||
},
|
||||
child: ViewInfoCard(
|
||||
fileServer: e.thumbFileServer,
|
||||
author: e.author,
|
||||
categories: _decodeCate(e.categories),
|
||||
path: e.thumbPath,
|
||||
title: e.title,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
final screen = NotificationListener(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('浏览记录'),
|
||||
actions: [
|
||||
IconButton(onPressed: _clearAll, icon: const Icon(Icons.auto_delete)),
|
||||
IconButton(
|
||||
onPressed: _clearAll, icon: const Icon(Icons.auto_delete)),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
physics: _scrollPhysics,
|
||||
controller: _scrollController,
|
||||
children: [
|
||||
Container(height: 10),
|
||||
ViewLogWrap(
|
||||
onTapComic: _chooseComic,
|
||||
comics: _comicList,
|
||||
onDelete: _clearOnce,
|
||||
),
|
||||
],
|
||||
children: entries.toList(),
|
||||
),
|
||||
),
|
||||
onNotification: (scrollNotification) {
|
||||
|
@ -151,88 +166,90 @@ class _ViewLogsScreenState extends State<ViewLogsScreen> {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<String> _decodeCate(String categories) {
|
||||
try {
|
||||
var decode = jsonDecode(categories);
|
||||
if (decode is List) {
|
||||
return List.of(decode).cast();
|
||||
}
|
||||
return [decode];
|
||||
} catch (e) {
|
||||
return [categories];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ViewLogWrap extends StatelessWidget {
|
||||
final Function(String) onTapComic;
|
||||
final List<ViewLogWrapEntity> comics;
|
||||
final Function(String id) onDelete;
|
||||
class ViewInfoCard extends StatelessWidget {
|
||||
final String fileServer;
|
||||
final String path;
|
||||
final String title;
|
||||
final String author;
|
||||
final List<String> categories;
|
||||
|
||||
const ViewLogWrap({
|
||||
const ViewInfoCard({
|
||||
Key? key,
|
||||
required this.onTapComic,
|
||||
required this.comics,
|
||||
required this.onDelete,
|
||||
required this.fileServer,
|
||||
required this.path,
|
||||
required this.title,
|
||||
required this.author,
|
||||
required this.categories,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var size = MediaQuery.of(context).size;
|
||||
var min = size.width < size.height ? size.width : size.height;
|
||||
var width = (min - 45) / 4;
|
||||
|
||||
var entries = comics.map((e) {
|
||||
return InkWell(
|
||||
key: e.key,
|
||||
onTap: () {
|
||||
onTapComic(e.id);
|
||||
},
|
||||
onLongPress: () {
|
||||
onDelete(e.id);
|
||||
},
|
||||
child: Card(
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
child: Column(
|
||||
var theme = Theme.of(context);
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
LayoutBuilder(builder:
|
||||
(BuildContext context, BoxConstraints constraints) {
|
||||
return RemoteImage(
|
||||
width: constraints.maxWidth,
|
||||
fileServer: e.fileServer,
|
||||
path: e.path);
|
||||
}),
|
||||
Text(
|
||||
e.title + '\n',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(height: 1.4),
|
||||
strutStyle: const StrutStyle(height: 1.4),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: RemoteImage(
|
||||
fileServer: fileServer,
|
||||
path: path,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: titleStyle),
|
||||
Container(height: 5),
|
||||
Text(author, style: authorStyle),
|
||||
Container(height: 5),
|
||||
Text.rich(
|
||||
TextSpan(text: "分类 : ${categories.join(' ')}"),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1!
|
||||
.color!
|
||||
.withAlpha(0xCC),
|
||||
),
|
||||
),
|
||||
Container(height: 5),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
Map<int, List<Widget>> map = {};
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
late List<Widget> list;
|
||||
if (i % 4 == 0) {
|
||||
list = [];
|
||||
map[i ~/ 4] = list;
|
||||
} else {
|
||||
list = map[i ~/ 4]!;
|
||||
}
|
||||
list.add(entries.elementAt(i));
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: map.values.map((e) => Wrap(
|
||||
alignment: WrapAlignment.spaceAround,
|
||||
children: e,
|
||||
)).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewLogWrapEntity {
|
||||
final Key key = UniqueKey();
|
||||
final String id;
|
||||
final String title;
|
||||
final String fileServer;
|
||||
final String path;
|
||||
|
||||
ViewLogWrapEntity(this.id, this.title, this.fileServer, this.path);
|
||||
}
|
||||
|
|
|
@ -253,6 +253,8 @@ class StreamComicPager extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _StreamComicPagerState extends State<StreamComicPager> {
|
||||
final TextEditingController _textEditController =
|
||||
TextEditingController(text: '');
|
||||
final _scrollController = ScrollController();
|
||||
late String _currentSort = SORT_DEFAULT;
|
||||
late int _currentPage = 1;
|
||||
|
@ -264,6 +266,12 @@ class _StreamComicPagerState extends State<StreamComicPager> {
|
|||
|
||||
// late Future<dynamic> _pageFuture;
|
||||
|
||||
_onSetOffset(int i) {
|
||||
_list.clear();
|
||||
_currentPage = i;
|
||||
_load();
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (_over || _error || _loading) {
|
||||
return;
|
||||
|
@ -317,6 +325,7 @@ class _StreamComicPagerState extends State<StreamComicPager> {
|
|||
void dispose() {
|
||||
_scrollController.removeListener(_onScroll);
|
||||
_scrollController.dispose();
|
||||
_textEditController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -366,10 +375,63 @@ class _StreamComicPagerState extends State<StreamComicPager> {
|
|||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_textEditController.clear();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
content: Card(
|
||||
child: TextField(
|
||||
controller: _textEditController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "请输入页数:",
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.allow(RegExp(r'\d+')),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('取消'),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
var text = _textEditController.text;
|
||||
if (text.isEmpty || text.length > 5) {
|
||||
return;
|
||||
}
|
||||
var num = int.parse(text);
|
||||
if (num == 0 || num > _maxPage) {
|
||||
return;
|
||||
}
|
||||
_currentPage = num;
|
||||
_onSetOffset(num);
|
||||
},
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Text("已经加载 ${_currentPage - 1} / $_maxPage 页"),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -178,7 +178,7 @@ packages:
|
|||
name: flutter_svg
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.1.1+1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -244,7 +244,7 @@ packages:
|
|||
name: image_picker_ios
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.5+5"
|
||||
version: "0.8.5+6"
|
||||
image_picker_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.0.0+1
|
||||
version: 1.0.1+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
|
Loading…
Reference in New Issue