This commit is contained in:
wgh136
2024-05-15 11:16:05 +08:00
parent 8f0e44216a
commit 2826f9f592
5 changed files with 263 additions and 26 deletions

View File

@@ -20,11 +20,28 @@ class _Appdata {
"useTranslatedNameForDownload": false, "useTranslatedNameForDownload": false,
}; };
bool lock = false;
void writeData() async { void writeData() async {
while (lock) {
await Future.delayed(const Duration(milliseconds: 20));
}
lock = true;
await File("${App.dataPath}/account.json") await File("${App.dataPath}/account.json")
.writeAsString(jsonEncode(account)); .writeAsString(jsonEncode(account));
await File("${App.dataPath}/settings.json") await File("${App.dataPath}/settings.json")
.writeAsString(jsonEncode(settings)); .writeAsString(jsonEncode(settings));
lock = false;
}
void writeSettings() async {
while (lock) {
await Future.delayed(const Duration(milliseconds: 20));
}
lock = true;
await File("${App.dataPath}/settings.json")
.writeAsString(jsonEncode(settings));
lock = false;
} }
Future<void> readData() async { Future<void> readData() async {
@@ -35,17 +52,17 @@ class _Appdata {
final settingsFile = File("${App.dataPath}/settings.json"); final settingsFile = File("${App.dataPath}/settings.json");
if (settingsFile.existsSync()) { if (settingsFile.existsSync()) {
var json = jsonDecode(await settingsFile.readAsString()); var json = jsonDecode(await settingsFile.readAsString());
for(var key in json.keys) { for (var key in json.keys) {
settings[key] = json[key]; settings[key] = json[key];
} }
} }
if(settings["downloadPath"] == null) { if (settings["downloadPath"] == null) {
settings["downloadPath"] = await _defaultDownloadPath; settings["downloadPath"] = await _defaultDownloadPath;
} }
} }
Future<String> get _defaultDownloadPath async{ Future<String> get _defaultDownloadPath async {
if(App.isAndroid) { if (App.isAndroid) {
String? downloadPath = "/storage/emulated/0/download"; String? downloadPath = "/storage/emulated/0/download";
if (!Directory(downloadPath).havePermission()) { if (!Directory(downloadPath).havePermission()) {
downloadPath = null; downloadPath = null;
@@ -53,14 +70,15 @@ class _Appdata {
var res = downloadPath; var res = downloadPath;
res ??= (await getExternalStorageDirectory())!.path; res ??= (await getExternalStorageDirectory())!.path;
return "$res/pixes"; return "$res/pixes";
} else if (App.isWindows){ } else if (App.isWindows) {
var res = await const MethodChannel("pixes/picture_folder").invokeMethod(""); var res =
if(res != "error") { await const MethodChannel("pixes/picture_folder").invokeMethod("");
if (res != "error") {
return res + "/pixes"; return res + "/pixes";
} }
} else if (App.isMacOS || App.isLinux) { } else if (App.isMacOS || App.isLinux) {
var downloadPath = (await getDownloadsDirectory())?.path; var downloadPath = (await getDownloadsDirectory())?.path;
if(downloadPath != null && Directory(downloadPath).havePermission()) { if (downloadPath != null && Directory(downloadPath).havePermission()) {
return "$downloadPath/pixes"; return "$downloadPath/pixes";
} }
} }

View File

@@ -9,6 +9,8 @@ export "state_controller.dart";
export "navigation.dart"; export "navigation.dart";
class _App { class _App {
final version = "1.0.0";
bool get isAndroid => Platform.isAndroid; bool get isAndroid => Platform.isAndroid;
bool get isIOS => Platform.isIOS; bool get isIOS => Platform.isIOS;
bool get isWindows => Platform.isWindows; bool get isWindows => Platform.isWindows;

View File

@@ -114,11 +114,15 @@ class DownloadingTask {
static String _generateFilePath(Illust illust, int index, String ext) { static String _generateFilePath(Illust illust, int index, String ext) {
final String downloadPath = appdata.settings["downloadPath"]; final String downloadPath = appdata.settings["downloadPath"];
String subPathPatten = appdata.settings["downloadSubPath"]; String subPathPatten = appdata.settings["downloadSubPath"];
final tags = appdata.settings["useTranslatedNameForDownload"] == false
? illust.tags.map((e) => e.name).toList()
: illust.tags.map((e) => e.translatedName ?? e.name).toList();
final tagsWeight = (appdata.settings["tagsWeight"] as String).split(' '); final tagsWeight = (appdata.settings["tagsWeight"] as String).split(' ');
tags.sort((a, b) => tagsWeight.indexOf(a) - tagsWeight.indexOf(b)); final originalTags = List<Tag>.from(illust.tags);
originalTags.sort((a, b){
return tagsWeight.indexOf(a.name) - tagsWeight.indexOf(b.name);
});
final tags = appdata.settings["useTranslatedNameForDownload"] == false
? originalTags.map((e) => e.name).toList()
: originalTags.map((e) => e.translatedName ?? e.name).toList();
subPathPatten = subPathPatten.replaceAll(r"${id}", illust.id.toString()); subPathPatten = subPathPatten.replaceAll(r"${id}", illust.id.toString());
subPathPatten = subPathPatten.replaceAll(r"${title}", illust.title); subPathPatten = subPathPatten.replaceAll(r"${title}", illust.title);
subPathPatten = subPathPatten.replaceAll(r"${author}", illust.author.name); subPathPatten = subPathPatten.replaceAll(r"${author}", illust.author.name);

View File

@@ -106,7 +106,14 @@ class Network {
}, },
options: Options( options: Options(
contentType: Headers.formUrlEncodedContentType, contentType: Headers.formUrlEncodedContentType,
validateStatus: (i) => true,
headers: headers)); headers: headers));
if(res.statusCode != 200) {
var data = res.data ?? "";
if(data.contains("Invalid refresh token")) {
throw "Failed to refresh token. Plaese logout and re-login";
}
}
var account = Account.fromJson(json.decode(res.data!)); var account = Account.fromJson(json.decode(res.data!));
appdata.account = account; appdata.account = account;
appdata.writeData(); appdata.writeData();

View File

@@ -1,9 +1,14 @@
import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart'; import 'package:fluent_ui/fluent_ui.dart';
import 'package:pixes/appdata.dart'; import 'package:pixes/appdata.dart';
import 'package:pixes/components/md.dart';
import 'package:pixes/components/message.dart';
import 'package:pixes/components/page_route.dart'; import 'package:pixes/components/page_route.dart';
import 'package:pixes/components/title_bar.dart'; import 'package:pixes/components/title_bar.dart';
import 'package:pixes/foundation/app.dart'; import 'package:pixes/foundation/app.dart';
import 'package:pixes/pages/main_page.dart'; import 'package:pixes/pages/main_page.dart';
import 'package:pixes/utils/io.dart';
import 'package:pixes/utils/translation.dart'; import 'package:pixes/utils/translation.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
@@ -24,7 +29,12 @@ class _SettingsPageState extends State<SettingsPage> {
SliverTitleBar(title: "Settings".tl), SliverTitleBar(title: "Settings".tl),
buildHeader("Account".tl), buildHeader("Account".tl),
buildAccount(), buildAccount(),
SliverPadding(padding: EdgeInsets.only(bottom: context.padding.bottom)), buildHeader("Download".tl),
buildDownload(),
buildHeader("About".tl),
buildAbout(),
SliverPadding(
padding: EdgeInsets.only(bottom: context.padding.bottom)),
], ],
), ),
); );
@@ -34,8 +44,10 @@ class _SettingsPageState extends State<SettingsPage> {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Text(text, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), child: Text(text,
),); style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
),
);
} }
Widget buildItem({required String title, String? subtitle, Widget? action}) { Widget buildItem({required String title, String? subtitle, Widget? action}) {
@@ -50,7 +62,7 @@ class _SettingsPageState extends State<SettingsPage> {
); );
} }
Widget buildAccount(){ Widget buildAccount() {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: Column( child: Column(
children: [ children: [
@@ -71,8 +83,7 @@ class _SettingsPageState extends State<SettingsPage> {
App.rootNavigatorKey.currentState!.pushAndRemoveUntil( App.rootNavigatorKey.currentState!.pushAndRemoveUntil(
AppPageRoute( AppPageRoute(
builder: (context) => const MainPage()), builder: (context) => const MainPage()),
(route) => false (route) => false);
);
}, },
), ),
FilledButton( FilledButton(
@@ -86,14 +97,209 @@ class _SettingsPageState extends State<SettingsPage> {
child: Text("Continue".tl).fixWidth(64), child: Text("Continue".tl).fixWidth(64),
), ),
), ),
buildItem(title: "Account Settings".tl, action: Button( buildItem(
child: Text("Edit".tl).fixWidth(64), title: "Account Settings".tl,
onPressed: (){ action: Button(
launchUrlString("https://www.pixiv.net/setting_user.php"); child: Text("Edit".tl).fixWidth(64),
}, onPressed: () {
)), launchUrlString("https://www.pixiv.net/setting_user.php");
},
)),
],
),
);
}
Widget buildDownload() {
return SliverToBoxAdapter(
child: Column(
children: [
buildItem(
title: "Download Path",
subtitle: appdata.settings["downloadPath"],
action: Button(
child: Text("Manage".tl).fixWidth(64),
onPressed: () {
if (Platform.isIOS) {
showToast(context, message: "Unsupport platform".tl);
return;
}
context.to(() => const _SetDownloadPathPage());
}),
),
buildItem(
title: "Subpath",
subtitle: appdata.settings["downloadSubPath"],
action: Button(
child: Text("Manage".tl).fixWidth(64),
onPressed: () {
context.to(() => const _SetDownloadSubPathPage());
}),
),
],
),
);
}
Widget buildAbout() {
return SliverToBoxAdapter(
child: Column(
children: [
buildItem(title: "Version", subtitle: App.version),
buildItem(
title: "Github",
action: IconButton(
icon: const Icon(MdIcons.open_in_new, size: 18,),
onPressed: () =>
launchUrlString("https://github.com/wgh136/pixes"),
)),
buildItem(
title: "Telegram",
action: IconButton(
icon: const Icon(MdIcons.open_in_new, size: 18,),
onPressed: () =>
launchUrlString("https://t.me/pica_group"),
)),
], ],
), ),
); );
} }
} }
class _SetDownloadPathPage extends StatefulWidget {
const _SetDownloadPathPage();
@override
State<_SetDownloadPathPage> createState() => __SetDownloadPathPageState();
}
class __SetDownloadPathPageState extends State<_SetDownloadPathPage> {
final controller =
TextEditingController(text: appdata.settings["downloadPath"]);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TitleBar(title: "Download Path".tl),
TextBox(
controller: controller,
).paddingHorizontal(16),
const SizedBox(
height: 8,
),
Button(
child: Text("Confirm".tl),
onPressed: () {
var text = controller.text;
if (Directory(text).havePermission()) {
appdata.settings["downloadPath"] = text;
appdata.writeData();
context.pop();
} else {
showToast(context, message: "No Permission".tl);
}
},
).toAlign(Alignment.centerRight).paddingRight(16),
],
);
}
}
class _SetDownloadSubPathPage extends StatefulWidget {
const _SetDownloadSubPathPage({super.key});
@override
State<_SetDownloadSubPathPage> createState() =>
__SetDownloadSubPathPageState();
}
class __SetDownloadSubPathPageState extends State<_SetDownloadSubPathPage> {
final controller =
TextEditingController(text: appdata.settings["downloadSubPath"]);
final controller2 =
TextEditingController(text: appdata.settings["tagsWeight"]);
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TitleBar(title: "Download Subpath".tl),
Text("Rule".tl)
.padding(const EdgeInsets.symmetric(vertical: 8, horizontal: 16)),
TextBox(
controller: controller,
).paddingHorizontal(16),
Text("Weights of the tags".tl)
.padding(const EdgeInsets.symmetric(vertical: 8, horizontal: 16)),
TextBox(
controller: controller2,
).paddingHorizontal(16),
const SizedBox(
height: 8,
),
ListTile(
title: Text("Use translated tag name".tl),
trailing: ToggleSwitch(
checked: appdata.settings["useTranslatedNameForDownload"],
onChanged: (value) {
appdata.settings["useTranslatedNameForDownload"] = value;
appdata.writeSettings();
},
),
),
const SizedBox(
height: 8,
),
Button(
child: Text("Confirm".tl),
onPressed: () {
var text = controller.text;
if (check(text)) {
appdata.settings["downloadSubPath"] = text;
appdata.writeData();
context.pop();
} else {
showToast(context, message: "No Permission".tl);
}
},
).toAlign(Alignment.centerRight).paddingRight(16),
const SizedBox(
height: 16,
),
Text(_instruction).paddingHorizontal(16)
],
),
);
}
bool check(String text) {
if (!text.startsWith('/') || !text.startsWith('\\')) {
return false;
}
return true;
}
String get _instruction => """
${"Edit the rule of where to store a image.".tl}
${"Note: The rule should contain file name.".tl}
${"Some keyword will be replaced as following rule:"}
\${title} -> ${"Title of the actwork".tl}
\${author} -> ${"Name of the author".tl}
\${id} -> ${"Actwork ID".tl}
\${index} -> ${"Index of the image in the artwork".tl}
\${ext} -> ${"File extension".tl}
${"Tags: Tags will be sorted with \"Weights of tags\" setting and be replaced with following rule".tl}
${"The final text will be affect by the setting og \"Use translated tag name\"".tl}
\${tag0} -> ${"The first tag of the artwork".tl}
\${tag1} -> ${"The sencondary tag of the artwork".tl}
${"Weights of the tags".tl}:
Filled with tags. The tags should be splited with a space. The tag in the front have higher weight.
It is required to use originlal name instead of translated name.
""";
}