Merge pull request #557 from venera-app/v1.5.3-dev

V1.5.3
This commit is contained in:
nyne
2025-10-13 20:42:31 +08:00
committed by GitHub
14 changed files with 312 additions and 312 deletions

View File

@@ -31,19 +31,30 @@ jobs:
- name: Update AltStore source
id: update_source
env:
GITHUB_TOKEN: ${{ secrets.ACTION_GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python update_alt_store.py
git config --global user.name 'GitHub Action'
git config --global user.email 'action@github.com'
git add alt_store.json
if git diff --staged --quiet; then
echo "changes=false" >> $GITHUB_OUTPUT
else
git commit -m "Updated source with latest release"
git push
echo "changes=true" >> $GITHUB_OUTPUT
fi
python update_alt_store.py
git config --global user.name 'GitHub Action'
git config --global user.email 'action@github.com'
git add alt_store.json
if git diff --staged --quiet; then
echo "changes=false" >> $GITHUB_OUTPUT
else
# Create a new branch for the PR
branch_name="update-altstore-$(date +%Y%m%d-%H%M%S)"
git checkout -b "$branch_name"
git commit -m "Updated source with latest release"
git push -u origin "$branch_name"
# Create PR using GitHub CLI
gh pr create \
--title "Update AltStore source with latest release" \
--body "This PR updates the alt_store.json file with the latest release information." \
--head "$branch_name" \
--base main
echo "changes=true" >> $GITHUB_OUTPUT
fi
- name: Calculate job duration
id: duration

View File

@@ -23,7 +23,7 @@ linter:
rules:
collection_methods_unrelated_type: false
use_build_context_synchronously: false
# avoid_print: false # Uncomment to disable the `avoid_print` rule
avoid_print: false
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at

View File

@@ -84,9 +84,8 @@ android {
buildTypes {
release {
// Temporarily solution to fix crash
minifyEnabled false
shrinkResources false
minifyEnabled true
shrinkResources true
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86_64"
}

View File

@@ -13,7 +13,7 @@ export "widget_utils.dart";
export "context.dart";
class _App {
final version = "1.5.2";
final version = "1.5.3";
bool get isAndroid => Platform.isAndroid;

View File

@@ -128,7 +128,7 @@ mixin _AppRouteTransitionMixin<T> on PageRoute<T> {
context,
animation,
secondaryAnimation,
enableIOSGesture
enableIOSGesture && App.isIOS
? IOSBackGestureDetector(
gestureWidth: _kBackGestureWidth,
enabledCallback: () => _isPopGestureEnabled<T>(this),
@@ -302,7 +302,7 @@ class _IOSBackGestureDetectorState extends State<IOSBackGestureDetector> {
assert(mounted);
assert(_backGestureController != null);
_backGestureController!.dragUpdate(
_convertToLogical(details.primaryDelta! / context.size!.width));
_convertToLogical(details.primaryDelta! / context.size!.width));
}
}

View File

@@ -17,39 +17,50 @@ class CategoriesPage extends StatefulWidget {
State<CategoriesPage> createState() => _CategoriesPageState();
}
class _CategoriesPageState extends State<CategoriesPage> {
class _CategoriesPageState extends State<CategoriesPage>
with
TickerProviderStateMixin,
AutomaticKeepAliveClientMixin<CategoriesPage> {
var categories = <String>[];
late TabController controller;
void onSettingsChanged() {
var categories =
List.from(appdata.settings["categories"]).whereType<String>().toList();
var categories = List.from(
appdata.settings["categories"],
).whereType<String>().toList();
var allCategories = ComicSource.all()
.map((e) => e.categoryData?.key)
.where((element) => element != null)
.map((e) => e!)
.toList();
categories =
categories.where((element) => allCategories.contains(element)).toList();
categories = categories
.where((element) => allCategories.contains(element))
.toList();
if (!categories.isEqualTo(this.categories)) {
setState(() {
this.categories = categories;
});
controller = TabController(length: categories.length, vsync: this);
}
}
@override
void initState() {
super.initState();
var categories =
List.from(appdata.settings["categories"]).whereType<String>().toList();
var categories = List.from(
appdata.settings["categories"],
).whereType<String>().toList();
var allCategories = ComicSource.all()
.map((e) => e.categoryData?.key)
.where((element) => element != null)
.map((e) => e!)
.toList();
this.categories =
categories.where((element) => allCategories.contains(element)).toList();
this.categories = categories
.where((element) => allCategories.contains(element))
.toList();
appdata.settings.addListener(onSettingsChanged);
controller = TabController(length: categories.length, vsync: this);
}
void addPage() {
@@ -59,6 +70,7 @@ class _CategoriesPageState extends State<CategoriesPage> {
@override
void dispose() {
super.dispose();
controller.dispose();
appdata.settings.removeListener(onSettingsChanged);
}
@@ -85,46 +97,45 @@ class _CategoriesPageState extends State<CategoriesPage> {
@override
Widget build(BuildContext context) {
super.build(context);
if (categories.isEmpty) {
return buildEmpty();
}
return Material(
child: DefaultTabController(
length: categories.length,
key: Key(categories.toString()),
child: Column(
children: [
AppTabBar(
key: PageStorageKey(categories.toString()),
tabs: categories.map((e) {
String title = e;
try {
title = getCategoryDataWithKey(e).title;
} catch (e) {
//
}
return Tab(
text: title,
key: Key(e),
);
}).toList(),
actionButton: TabActionButton(
icon: const Icon(Icons.add),
text: "Add".tl,
onPressed: addPage,
),
).paddingTop(context.padding.top),
Expanded(
child: TabBarView(
children: categories.map((e) => _CategoryPage(e)).toList(),
),
)
],
),
child: Column(
children: [
AppTabBar(
controller: controller,
key: PageStorageKey(categories.toString()),
tabs: categories.map((e) {
String title = e;
try {
title = getCategoryDataWithKey(e).title;
} catch (e) {
//
}
return Tab(text: title, key: Key(e));
}).toList(),
actionButton: TabActionButton(
icon: const Icon(Icons.add),
text: "Add".tl,
onPressed: addPage,
),
).paddingTop(context.padding.top),
Expanded(
child: TabBarView(
controller: controller,
children: categories.map((e) => _CategoryPage(e)).toList(),
),
),
],
),
);
}
@override
bool get wantKeepAlive => true;
}
typedef ClickTagCallback = void Function(String, String?);
@@ -150,38 +161,42 @@ class _CategoryPage extends StatelessWidget {
var children = <Widget>[];
if (data.enableRankingPage || data.buttons.isNotEmpty) {
children.add(buildTitle(data.title));
children.add(Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 16),
child: Wrap(
children: [
if (data.enableRankingPage)
buildTag("Ranking".tl, () {
context.to(() => RankingPage(categoryKey: data.key));
}),
for (var buttonData in data.buttons)
buildTag(buttonData.label.tl, buttonData.onTap)
],
children.add(
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 16),
child: Wrap(
children: [
if (data.enableRankingPage)
buildTag("Ranking".tl, () {
context.to(() => RankingPage(categoryKey: data.key));
}),
for (var buttonData in data.buttons)
buildTag(buttonData.label.tl, buttonData.onTap),
],
),
),
));
);
}
for (var part in data.categories) {
if (part.enableRandom) {
children.add(StatefulBuilder(builder: (context, updater) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildTitleWithRefresh(part.title, () => updater(() {})),
buildTags(part.categories)
],
);
}));
children.add(
StatefulBuilder(
builder: (context, updater) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildTitleWithRefresh(part.title, () => updater(() {})),
buildTags(part.categories),
],
);
},
),
);
} else {
children.add(buildTitle(part.title));
children.add(
buildTags(part.categories),
);
children.add(buildTags(part.categories));
}
}
return SingleChildScrollView(
@@ -195,8 +210,10 @@ class _CategoryPage extends StatelessWidget {
Widget buildTitle(String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 10, 5, 10),
child: Text(title.tl,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500)),
child: Text(
title.tl,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
);
}
@@ -207,21 +224,16 @@ class _CategoryPage extends StatelessWidget {
children: [
Text(
title.tl,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
const Spacer(),
IconButton(onPressed: onRefresh, icon: const Icon(Icons.refresh))
IconButton(onPressed: onRefresh, icon: const Icon(Icons.refresh)),
],
),
);
}
Widget buildTags(
List<CategoryItem> categories,
) {
Widget buildTags(List<CategoryItem> categories) {
return Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 16),
child: Wrap(

View File

@@ -155,64 +155,60 @@ abstract mixin class _ComicPageActions {
builder: (context, setState) {
return ContentDialog(
title: "Download".tl,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<int>(
value: -1,
groupValue: selected,
title: Text("Normal".tl),
onChanged: (v) {
setState(() {
selected = v!;
});
},
),
ExpansionTile(
title: Text("Archive".tl),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
content: RadioGroup<int>(
groupValue: selected,
onChanged: (v) {
setState(() {
selected = v ?? selected;
});
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<int>(
value: -1,
title: Text("Normal".tl),
),
collapsedShape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
onExpansionChanged: (b) {
if (!isLoading && b && archives == null) {
isLoading = true;
comicSource.archiveDownloader!
.getArchives(comic.id)
.then((value) {
if (value.success) {
archives = value.data;
} else {
App.rootContext
.showMessage(message: value.errorMessage!);
}
setState(() {
isLoading = false;
ExpansionTile(
title: Text("Archive".tl),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
collapsedShape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
onExpansionChanged: (b) {
if (!isLoading && b && archives == null) {
isLoading = true;
comicSource.archiveDownloader!
.getArchives(comic.id)
.then((value) {
if (value.success) {
archives = value.data;
} else {
App.rootContext
.showMessage(message: value.errorMessage!);
}
setState(() {
isLoading = false;
});
});
});
}
},
children: [
if (archives == null)
const ListLoadingIndicator().toCenter()
else
for (int i = 0; i < archives!.length; i++)
RadioListTile<int>(
value: i,
groupValue: selected,
onChanged: (v) {
setState(() {
selected = v!;
});
},
title: Text(archives![i].title),
subtitle: Text(archives![i].description),
)
],
)
],
}
},
children: [
if (archives == null)
const ListLoadingIndicator().toCenter()
else
for (int i = 0; i < archives!.length; i++)
RadioListTile<int>(
value: i,
title: Text(archives![i].title),
subtitle: Text(archives![i].description),
)
],
)
],
),
),
actions: [
Button.filled(
@@ -237,10 +233,12 @@ abstract mixin class _ComicPageActions {
isGettingLink = false;
});
} else if (context.mounted) {
LocalManager()
if (res.data.isNotEmpty) {
LocalManager()
.addTask(ArchiveDownloadTask(res.data, comic));
App.rootContext
App.rootContext
.showMessage(message: "Download started".tl);
}
context.pop();
}
},

View File

@@ -514,51 +514,53 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
child: CircularProgressIndicator(),
),
)
: Column(
key: key,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 600),
...List.generate(importMethods.length, (index) {
return RadioListTile(
title: Text(importMethods[index]),
value: index,
groupValue: type,
onChanged: (value) {
setState(() {
type = value as int;
});
},
);
}),
if (type != 4)
ListTile(
title: Text("Add to favorites".tl),
trailing: Select(
current: selectedFolder,
values: folders,
minWidth: 112,
onTap: (v) {
setState(() {
selectedFolder = folders[v];
});
},
),
).paddingHorizontal(8),
if (!App.isIOS && !App.isMacOS && type != 2 && type != 3)
CheckboxListTile(
enabled: true,
title: Text("Copy to app local path".tl),
value: copyToLocalFolder,
onChanged: (v) {
setState(() {
copyToLocalFolder = !copyToLocalFolder;
});
}).paddingHorizontal(8),
const SizedBox(height: 8),
Text(info).paddingHorizontal(24),
],
),
: RadioGroup<int>(
groupValue: type,
onChanged: (value) {
setState(() {
type = value ?? type;
});
},
child: Column(
key: key,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 600),
...List.generate(importMethods.length, (index) {
return RadioListTile<int>(
title: Text(importMethods[index]),
value: index,
);
}),
if (type != 4)
ListTile(
title: Text("Add to favorites".tl),
trailing: Select(
current: selectedFolder,
values: folders,
minWidth: 112,
onTap: (v) {
setState(() {
selectedFolder = folders[v];
});
},
),
).paddingHorizontal(8),
if (!App.isIOS && !App.isMacOS && type != 2 && type != 3)
CheckboxListTile(
enabled: true,
title: Text("Copy to app local path".tl),
value: copyToLocalFolder,
onChanged: (v) {
setState(() {
copyToLocalFolder = !copyToLocalFolder;
});
}).paddingHorizontal(8),
const SizedBox(height: 8),
Text(info).paddingHorizontal(24),
],
),
),
actions: [
Button.text(
child: Row(

View File

@@ -404,21 +404,23 @@ class _ImageFavoritesDialogState extends State<_ImageFavoritesDialog> {
children: [
tabBar,
TabViewBody(children: [
Column(
children: ImageFavoriteSortType.values
.map(
(e) => RadioListTile<ImageFavoriteSortType>(
title: Text(e.value.tl),
value: e,
groupValue: sortType,
onChanged: (v) {
setState(() {
sortType = v!;
});
},
),
)
.toList(),
RadioGroup<ImageFavoriteSortType>(
groupValue: sortType,
onChanged: (v) {
setState(() {
sortType = v ?? sortType;
});
},
child: Column(
children: ImageFavoriteSortType.values
.map(
(e) => RadioListTile<ImageFavoriteSortType>(
title: Text(e.value.tl),
value: e,
),
)
.toList(),
),
),
Column(
children: [

View File

@@ -70,39 +70,29 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
return StatefulBuilder(builder: (context, setState) {
return ContentDialog(
title: "Sort".tl,
content: Column(
children: [
RadioListTile<LocalSortType>(
title: Text("Name".tl),
value: LocalSortType.name,
groupValue: sortType,
onChanged: (v) {
setState(() {
sortType = v!;
});
},
),
RadioListTile<LocalSortType>(
title: Text("Date".tl),
value: LocalSortType.timeAsc,
groupValue: sortType,
onChanged: (v) {
setState(() {
sortType = v!;
});
},
),
RadioListTile<LocalSortType>(
title: Text("Date Desc".tl),
value: LocalSortType.timeDesc,
groupValue: sortType,
onChanged: (v) {
setState(() {
sortType = v!;
});
},
),
],
content: RadioGroup<LocalSortType>(
groupValue: sortType,
onChanged: (v) {
setState(() {
sortType = v ?? sortType;
});
},
child: Column(
children: [
RadioListTile<LocalSortType>(
title: Text("Name".tl),
value: LocalSortType.name,
),
RadioListTile<LocalSortType>(
title: Text("Date".tl),
value: LocalSortType.timeAsc,
),
RadioListTile<LocalSortType>(
title: Text("Date Desc".tl),
value: LocalSortType.timeDesc,
),
],
),
),
actions: [
FilledButton(

View File

@@ -428,30 +428,26 @@ class _WebdavSettingState extends State<_WebdavSetting> {
),
),
const SizedBox(height: 12),
Row(
children: [
Text("Operation".tl),
Radio<bool>(
groupValue: upload,
value: true,
onChanged: (value) {
setState(() {
upload = value!;
});
},
),
Text("Upload".tl),
Radio<bool>(
groupValue: upload,
value: false,
onChanged: (value) {
setState(() {
upload = value!;
});
},
),
Text("Download".tl),
],
RadioGroup<bool>(
groupValue: upload,
onChanged: (value) {
setState(() {
upload = value ?? upload;
});
},
child: Row(
children: [
Text("Operation".tl),
Radio<bool>(
value: true,
),
Text("Upload".tl),
Radio<bool>(
value: false,
),
Text("Download".tl),
],
),
),
const SizedBox(height: 16),
AnimatedSize(

View File

@@ -111,44 +111,34 @@ class _ProxySettingViewState extends State<_ProxySettingView> {
return PopUpWidgetScaffold(
title: "Proxy".tl,
body: SingleChildScrollView(
child: Column(
children: [
RadioListTile<String>(
title: Text("Direct".tl),
value: 'direct',
groupValue: type,
onChanged: (v) {
setState(() {
type = v!;
});
appdata.settings['proxy'] = toProxyStr();
appdata.saveData();
},
),
RadioListTile<String>(
title: Text("System".tl),
value: 'system',
groupValue: type,
onChanged: (v) {
setState(() {
type = v!;
});
appdata.settings['proxy'] = toProxyStr();
appdata.saveData();
},
),
RadioListTile(
title: Text("Manual".tl),
value: 'manual',
groupValue: type,
onChanged: (v) {
setState(() {
type = v!;
});
},
),
if (type == 'manual') buildManualProxy(),
],
child: RadioGroup<String>(
groupValue: type,
onChanged: (v) {
setState(() {
type = v ?? type;
});
if (type != 'manual') {
appdata.settings['proxy'] = toProxyStr();
appdata.saveData();
}
},
child: Column(
children: [
RadioListTile<String>(
title: Text("Direct".tl),
value: 'direct',
),
RadioListTile<String>(
title: Text("System".tl),
value: 'system',
),
RadioListTile(
title: Text("Manual".tl),
value: 'manual',
),
if (type == 'manual') buildManualProxy(),
],
),
),
),
);

View File

@@ -416,10 +416,10 @@ packages:
dependency: "direct main"
description:
name: flutter_memory_info
sha256: "1f112f1d7503aa1681fc8e923f6cd0e847bb2fbeec3753ed021cf1e5f7e9cd74"
sha256: eacfd0dd01ff596b4e5bf022442769a1807a73f2af43d62802436f0a5de99137
url: "https://pub.dev"
source: hosted
version: "0.0.1"
version: "0.0.3"
flutter_plugin_android_lifecycle:
dependency: transitive
description:

View File

@@ -2,7 +2,7 @@ name: venera
description: "A comic app."
publish_to: 'none'
version: 1.5.2+152
version: 1.5.3+153
environment:
sdk: '>=3.8.0 <4.0.0'
@@ -75,7 +75,7 @@ dependencies:
ref: fe182cdf40e5fa6230f451bc1d643b860f610d13
dynamic_color: ^1.7.0
shimmer_animation: ^2.1.0
flutter_memory_info: ^0.0.1
flutter_memory_info: ^0.0.3
syntax_highlight: ^0.4.0
flutter_7zip:
git: