mirror of
https://github.com/venera-app/venera.git
synced 2025-09-28 00:07:24 +00:00
@@ -1576,7 +1576,7 @@ class _SelectDownloadChapterState extends State<_SelectDownloadChapter> {
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
onPressed: selected.isEmpty ? null : () {
|
||||
widget.finishSelect(selected);
|
||||
context.pop();
|
||||
},
|
||||
@@ -1587,7 +1587,7 @@ class _SelectDownloadChapterState extends State<_SelectDownloadChapter> {
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: MediaQuery.of(context).padding.bottom + 4),
|
||||
SizedBox(height: MediaQuery.of(context).padding.bottom),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@@ -14,11 +14,11 @@ import 'package:venera/utils/translations.dart';
|
||||
class ComicSourcePage extends StatefulWidget {
|
||||
const ComicSourcePage({super.key});
|
||||
|
||||
static void checkComicSourceUpdate([bool showLoading = false]) async {
|
||||
static Future<void> checkComicSourceUpdate([bool implicit = false]) async {
|
||||
if (ComicSource.all().isEmpty) {
|
||||
return;
|
||||
}
|
||||
var controller = showLoading ? showLoadingDialog(App.rootContext) : null;
|
||||
var controller = implicit ? null : showLoadingDialog(App.rootContext);
|
||||
var dio = AppDio();
|
||||
var res = await dio.get<String>(
|
||||
"https://raw.githubusercontent.com/venera-app/venera-configs/master/index.json");
|
||||
@@ -40,7 +40,9 @@ class ComicSourcePage extends StatefulWidget {
|
||||
}
|
||||
controller?.close();
|
||||
if (shouldUpdate.isEmpty) {
|
||||
App.rootContext.showMessage(message: "No Update Available".tl);
|
||||
if(!implicit) {
|
||||
App.rootContext.showMessage(message: "No Update Available".tl);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var msg = "";
|
||||
@@ -48,10 +50,11 @@ class ComicSourcePage extends StatefulWidget {
|
||||
msg += "${ComicSource.find(key)?.name}: v${versions[key]}\n";
|
||||
}
|
||||
msg = msg.trim();
|
||||
showConfirmDialog(
|
||||
await showConfirmDialog(
|
||||
context: App.rootContext,
|
||||
title: "Updates Available".tl,
|
||||
content: msg,
|
||||
confirmText: "Update",
|
||||
onConfirm: () {
|
||||
for (var key in shouldUpdate) {
|
||||
var source = ComicSource.find(key);
|
||||
@@ -104,7 +107,7 @@ class _BodyState extends State<_Body> {
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.update_outlined),
|
||||
title: Text("Check updates".tl),
|
||||
onTap: () => ComicSourcePage.checkComicSourceUpdate(true),
|
||||
onTap: () => ComicSourcePage.checkComicSourceUpdate(false),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
),
|
||||
);
|
||||
@@ -161,71 +164,76 @@ class _BodyState extends State<_Body> {
|
||||
for (var item in source.settings!.entries) {
|
||||
var key = item.key;
|
||||
String type = item.value['type'];
|
||||
if (type == "select") {
|
||||
var current = source.data['settings'][key];
|
||||
if (current == null) {
|
||||
var d = item.value['default'];
|
||||
for (var option in item.value['options']) {
|
||||
if (option['value'] == d) {
|
||||
current = option['text'] ?? option['value'];
|
||||
break;
|
||||
try {
|
||||
if (type == "select") {
|
||||
var current = source.data['settings'][key];
|
||||
if (current == null) {
|
||||
var d = item.value['default'];
|
||||
for (var option in item.value['options']) {
|
||||
if (option['value'] == d) {
|
||||
current = option['text'] ?? option['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
yield ListTile(
|
||||
title: Text((item.value['title'] as String).ts(source.key)),
|
||||
trailing: Select(
|
||||
current: (current as String).ts(source.key),
|
||||
values: (item.value['options'] as List)
|
||||
.map<String>(
|
||||
(e) => ((e['text'] ?? e['value']) as String).ts(source.key))
|
||||
.toList(),
|
||||
onTap: (i) {
|
||||
source.data['settings'][key] = item.value['options'][i]['value'];
|
||||
source.saveData();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (type == "switch") {
|
||||
var current = source.data['settings'][key] ?? item.value['default'];
|
||||
yield ListTile(
|
||||
title: Text((item.value['title'] as String).ts(source.key)),
|
||||
trailing: Switch(
|
||||
value: current,
|
||||
onChanged: (v) {
|
||||
source.data['settings'][key] = v;
|
||||
source.saveData();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (type == "input") {
|
||||
var current =
|
||||
source.data['settings'][key] ?? item.value['default'] ?? '';
|
||||
yield ListTile(
|
||||
title: Text((item.value['title'] as String).ts(source.key)),
|
||||
subtitle: Text(current, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
showInputDialog(
|
||||
context: context,
|
||||
title: (item.value['title'] as String).ts(source.key),
|
||||
initialValue: current,
|
||||
inputValidator: item.value['validator'] == null
|
||||
? null
|
||||
: RegExp(item.value['validator']),
|
||||
onConfirm: (value) {
|
||||
source.data['settings'][key] = value;
|
||||
source.saveData();
|
||||
setState(() {});
|
||||
return null;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
yield ListTile(
|
||||
title: Text((item.value['title'] as String).ts(source.key)),
|
||||
trailing: Select(
|
||||
current: (current as String).ts(source.key),
|
||||
values: (item.value['options'] as List)
|
||||
.map<String>(
|
||||
(e) => ((e['text'] ?? e['value']) as String).ts(source.key))
|
||||
.toList(),
|
||||
onTap: (i) {
|
||||
source.data['settings'][key] = item.value['options'][i]['value'];
|
||||
source.saveData();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (type == "switch") {
|
||||
var current = source.data['settings'][key] ?? item.value['default'];
|
||||
yield ListTile(
|
||||
title: Text((item.value['title'] as String).ts(source.key)),
|
||||
trailing: Switch(
|
||||
value: current,
|
||||
onChanged: (v) {
|
||||
source.data['settings'][key] = v;
|
||||
source.saveData();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (type == "input") {
|
||||
var current =
|
||||
source.data['settings'][key] ?? item.value['default'] ?? '';
|
||||
yield ListTile(
|
||||
title: Text((item.value['title'] as String).ts(source.key)),
|
||||
subtitle: Text(current, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
showInputDialog(
|
||||
context: context,
|
||||
title: (item.value['title'] as String).ts(source.key),
|
||||
initialValue: current,
|
||||
inputValidator: item.value['validator'] == null
|
||||
? null
|
||||
: RegExp(item.value['validator']),
|
||||
onConfirm: (value) {
|
||||
source.data['settings'][key] = value;
|
||||
source.saveData();
|
||||
setState(() {});
|
||||
return null;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
catch(e, s) {
|
||||
Log.error("ComicSourcePage", "Failed to build a setting\n$e\n$s");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,10 +454,11 @@ class _ComicSourceListState extends State<_ComicSourceList> {
|
||||
itemBuilder: (context, index) {
|
||||
var key = json![index]["key"];
|
||||
var action = currentKey.contains(key)
|
||||
? const Icon(Icons.check)
|
||||
? const Icon(Icons.check, size: 20).paddingRight(8)
|
||||
: Tooltip(
|
||||
message: "Add",
|
||||
child: IconButton(
|
||||
child: Button.icon(
|
||||
color: context.colorScheme.primary,
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
await widget.onAdd(
|
||||
|
@@ -223,6 +223,9 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
|
||||
@override
|
||||
void handleLongPressDown(Offset location) {
|
||||
if(!appdata.settings['enableLongPressToZoom']) {
|
||||
return;
|
||||
}
|
||||
var photoViewController = photoViewControllers[reader.page]!;
|
||||
double target = photoViewController.getInitialScale!.call()! * 1.75;
|
||||
var size = MediaQuery.of(context).size;
|
||||
@@ -234,6 +237,9 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
|
||||
@override
|
||||
void handleLongPressUp(Offset location) {
|
||||
if(!appdata.settings['enableLongPressToZoom']) {
|
||||
return;
|
||||
}
|
||||
var photoViewController = photoViewControllers[reader.page]!;
|
||||
double target = photoViewController.getInitialScale!.call()!;
|
||||
photoViewController.animateScale?.call(target);
|
||||
@@ -509,6 +515,9 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
|
||||
@override
|
||||
void handleLongPressDown(Offset location) {
|
||||
if(!appdata.settings['enableLongPressToZoom']) {
|
||||
return;
|
||||
}
|
||||
double target = photoViewController.getInitialScale!.call()! * 1.75;
|
||||
var size = MediaQuery.of(context).size;
|
||||
photoViewController.animateScale?.call(
|
||||
@@ -519,6 +528,9 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
|
||||
@override
|
||||
void handleLongPressUp(Offset location) {
|
||||
if(!appdata.settings['enableLongPressToZoom']) {
|
||||
return;
|
||||
}
|
||||
double target = photoViewController.getInitialScale!.call()!;
|
||||
photoViewController.animateScale?.call(target);
|
||||
}
|
||||
|
@@ -393,7 +393,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
||||
|
||||
Widget buildPageInfoText() {
|
||||
var epName = context.reader.widget.chapters?.values
|
||||
.elementAt(context.reader.chapter - 1) ??
|
||||
.elementAtOrNull(context.reader.chapter - 1) ??
|
||||
"E${context.reader.chapter}";
|
||||
if (epName.length > 8) {
|
||||
epName = "${epName.substring(0, 8)}...";
|
||||
|
@@ -53,30 +53,7 @@ class _AboutSettingsState extends State<AboutSettings> {
|
||||
setState(() {
|
||||
isCheckingUpdate = true;
|
||||
});
|
||||
checkUpdate().then((value) {
|
||||
if (value) {
|
||||
showDialog(
|
||||
context: App.rootContext,
|
||||
builder: (context) {
|
||||
return ContentDialog(
|
||||
title: "New version available".tl,
|
||||
content: Text(
|
||||
"A new version is available. Do you want to update now?"
|
||||
.tl),
|
||||
actions: [
|
||||
Button.text(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
launchUrlString(
|
||||
"https://github.com/venera-app/venera/releases");
|
||||
},
|
||||
child: Text("Update".tl),
|
||||
),
|
||||
]);
|
||||
});
|
||||
} else {
|
||||
context.showMessage(message: "No new version available".tl);
|
||||
}
|
||||
checkUpdateUi().then((value) {
|
||||
setState(() {
|
||||
isCheckingUpdate = false;
|
||||
});
|
||||
@@ -108,6 +85,33 @@ Future<bool> checkUpdate() async {
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> checkUpdateUi([bool showMessageIfNoUpdate = true]) async {
|
||||
var value = await checkUpdate();
|
||||
if (value) {
|
||||
showDialog(
|
||||
context: App.rootContext,
|
||||
builder: (context) {
|
||||
return ContentDialog(
|
||||
title: "New version available".tl,
|
||||
content: Text(
|
||||
"A new version is available. Do you want to update now?".tl),
|
||||
actions: [
|
||||
Button.text(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
launchUrlString(
|
||||
"https://github.com/venera-app/venera/releases");
|
||||
},
|
||||
child: Text("Update".tl),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
} else if (showMessageIfNoUpdate) {
|
||||
App.rootContext.showMessage(message: "No new version available".tl);
|
||||
}
|
||||
}
|
||||
|
||||
/// return true if version1 > version2
|
||||
bool _compareVersion(String version1, String version2) {
|
||||
var v1 = version1.split(".");
|
||||
|
@@ -17,6 +17,13 @@ class _NetworkSettingsState extends State<NetworkSettings> {
|
||||
title: "Proxy".tl,
|
||||
builder: () => const _ProxySettingView(),
|
||||
).toSliver(),
|
||||
_SliderSetting(
|
||||
title: "Download Threads".tl,
|
||||
settingsIndex: 'downloadThreads',
|
||||
interval: 1,
|
||||
min: 1,
|
||||
max: 16,
|
||||
).toSliver(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -42,50 +49,50 @@ class _ProxySettingViewState extends State<_ProxySettingView> {
|
||||
|
||||
// USERNAME:PASSWORD@HOST:PORT
|
||||
String toProxyStr() {
|
||||
if(type == 'direct') {
|
||||
if (type == 'direct') {
|
||||
return 'direct';
|
||||
} else if(type == 'system') {
|
||||
} else if (type == 'system') {
|
||||
return 'system';
|
||||
}
|
||||
var res = '';
|
||||
if(username.isNotEmpty) {
|
||||
if (username.isNotEmpty) {
|
||||
res += username;
|
||||
if(password.isNotEmpty) {
|
||||
if (password.isNotEmpty) {
|
||||
res += ':$password';
|
||||
}
|
||||
res += '@';
|
||||
}
|
||||
res += host;
|
||||
if(port.isNotEmpty) {
|
||||
if (port.isNotEmpty) {
|
||||
res += ':$port';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void parseProxyString(String proxy) {
|
||||
if(proxy == 'direct') {
|
||||
if (proxy == 'direct') {
|
||||
type = 'direct';
|
||||
return;
|
||||
} else if(proxy == 'system') {
|
||||
} else if (proxy == 'system') {
|
||||
type = 'system';
|
||||
return;
|
||||
}
|
||||
type = 'manual';
|
||||
var parts = proxy.split('@');
|
||||
if(parts.length == 2) {
|
||||
if (parts.length == 2) {
|
||||
var auth = parts[0].split(':');
|
||||
if(auth.length == 2) {
|
||||
if (auth.length == 2) {
|
||||
username = auth[0];
|
||||
password = auth[1];
|
||||
}
|
||||
parts = parts[1].split(':');
|
||||
if(parts.length == 2) {
|
||||
if (parts.length == 2) {
|
||||
host = parts[0];
|
||||
port = parts[1];
|
||||
}
|
||||
} else {
|
||||
parts = proxy.split(':');
|
||||
if(parts.length == 2) {
|
||||
if (parts.length == 2) {
|
||||
host = parts[0];
|
||||
port = parts[1];
|
||||
}
|
||||
@@ -140,7 +147,7 @@ class _ProxySettingViewState extends State<_ProxySettingView> {
|
||||
});
|
||||
},
|
||||
),
|
||||
if(type == 'manual') buildManualProxy(),
|
||||
if (type == 'manual') buildManualProxy(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -164,7 +171,7 @@ class _ProxySettingViewState extends State<_ProxySettingView> {
|
||||
host = v;
|
||||
},
|
||||
validator: (v) {
|
||||
if(v?.isEmpty ?? false) {
|
||||
if (v?.isEmpty ?? false) {
|
||||
return "Host cannot be empty".tl;
|
||||
}
|
||||
return null;
|
||||
@@ -181,10 +188,10 @@ class _ProxySettingViewState extends State<_ProxySettingView> {
|
||||
port = v;
|
||||
},
|
||||
validator: (v) {
|
||||
if(v?.isEmpty ?? true) {
|
||||
if (v?.isEmpty ?? true) {
|
||||
return null;
|
||||
}
|
||||
if(int.tryParse(v!) == null) {
|
||||
if (int.tryParse(v!) == null) {
|
||||
return "Port must be a number".tl;
|
||||
}
|
||||
return null;
|
||||
@@ -201,7 +208,7 @@ class _ProxySettingViewState extends State<_ProxySettingView> {
|
||||
username = v;
|
||||
},
|
||||
validator: (v) {
|
||||
if((v?.isEmpty ?? false) && password.isNotEmpty) {
|
||||
if ((v?.isEmpty ?? false) && password.isNotEmpty) {
|
||||
return "Username cannot be empty".tl;
|
||||
}
|
||||
return null;
|
||||
@@ -221,7 +228,7 @@ class _ProxySettingViewState extends State<_ProxySettingView> {
|
||||
const SizedBox(height: 16),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
if(formKey.currentState?.validate() ?? false) {
|
||||
if (formKey.currentState?.validate() ?? false) {
|
||||
appdata.settings['proxy'] = toProxyStr();
|
||||
appdata.saveData();
|
||||
App.rootContext.pop();
|
||||
|
@@ -54,6 +54,13 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
||||
widget.onChanged?.call("autoPageTurningInterval");
|
||||
},
|
||||
).toSliver(),
|
||||
_SwitchSetting(
|
||||
title: 'Long press to zoom'.tl,
|
||||
settingKey: 'enableLongPressToZoom',
|
||||
onChanged: () {
|
||||
widget.onChanged?.call('enableLongPressToZoom');
|
||||
},
|
||||
).toSliver(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user