mirror of
https://github.com/wgh136/pixes.git
synced 2025-09-27 12:57:24 +00:00
@@ -152,6 +152,7 @@ abstract class MultiPageLoadingState<T extends StatefulWidget, S extends Object>
|
|||||||
|
|
||||||
void firstLoad() {
|
void firstLoad() {
|
||||||
loadData(_page).then((value) {
|
loadData(_page).then((value) {
|
||||||
|
if (!mounted) return;
|
||||||
if(value.success) {
|
if(value.success) {
|
||||||
_page++;
|
_page++;
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@@ -35,15 +35,15 @@ extension NovelExt on Network {
|
|||||||
return getNovelsWithNextUrl(url);
|
return getNovelsWithNextUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Res<List<Novel>>> getBookmarkedNovels(String uid) {
|
Future<Res<List<Novel>>> getBookmarkedNovels(String uid, bool public) {
|
||||||
return getNovelsWithNextUrl(
|
return getNovelsWithNextUrl(
|
||||||
"/v1/user/bookmarks/novel?user_id=$uid&restrict=public");
|
"/v1/user/bookmarks/novel?user_id=$uid&restrict=${public ? "public" : "private"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Res<bool>> favoriteNovel(String id) async {
|
Future<Res<bool>> favoriteNovel(String id, bool public) async {
|
||||||
var res = await apiPost("/v2/novel/bookmark/add", data: {
|
var res = await apiPost("/v2/novel/bookmark/add", data: {
|
||||||
"novel_id": id,
|
"novel_id": id,
|
||||||
"restrict": "public",
|
"restrict": public ? "public" : "private",
|
||||||
});
|
});
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
return Res.fromErrorRes(res);
|
return Res.fromErrorRes(res);
|
||||||
|
@@ -3,6 +3,7 @@ import 'package:pixes/appdata.dart';
|
|||||||
import 'package:pixes/components/grid.dart';
|
import 'package:pixes/components/grid.dart';
|
||||||
import 'package:pixes/components/loading.dart';
|
import 'package:pixes/components/loading.dart';
|
||||||
import 'package:pixes/components/novel.dart';
|
import 'package:pixes/components/novel.dart';
|
||||||
|
import 'package:pixes/components/segmented_button.dart';
|
||||||
import 'package:pixes/components/title_bar.dart';
|
import 'package:pixes/components/title_bar.dart';
|
||||||
import 'package:pixes/foundation/widget_utils.dart';
|
import 'package:pixes/foundation/widget_utils.dart';
|
||||||
import 'package:pixes/network/network.dart';
|
import 'package:pixes/network/network.dart';
|
||||||
@@ -17,11 +18,41 @@ class NovelBookmarksPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _NovelBookmarksPageState
|
class _NovelBookmarksPageState
|
||||||
extends MultiPageLoadingState<NovelBookmarksPage, Novel> {
|
extends MultiPageLoadingState<NovelBookmarksPage, Novel> {
|
||||||
|
bool public = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildFrame(BuildContext context, Widget child) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
TitleBar(
|
||||||
|
title: "Bookmarks".tl,
|
||||||
|
action: SegmentedButton(
|
||||||
|
options: [
|
||||||
|
SegmentedButtonOption("public", "Public".tl),
|
||||||
|
SegmentedButtonOption("private", "Private".tl),
|
||||||
|
],
|
||||||
|
onPressed: (key) {
|
||||||
|
var newPublic = key == "public";
|
||||||
|
if (newPublic != public) {
|
||||||
|
public = newPublic;
|
||||||
|
nextUrl = null;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
value: public ? "public" : "private",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: child,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildContent(BuildContext context, List<Novel> data) {
|
Widget buildContent(BuildContext context, List<Novel> data) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
TitleBar(title: "Bookmarks".tl),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GridViewWithFixedItemHeight(
|
child: GridViewWithFixedItemHeight(
|
||||||
itemCount: data.length,
|
itemCount: data.length,
|
||||||
@@ -45,7 +76,7 @@ class _NovelBookmarksPageState
|
|||||||
Future<Res<List<Novel>>> loadData(int page) async {
|
Future<Res<List<Novel>>> loadData(int page) async {
|
||||||
if (nextUrl == "end") return Res.error("No more data");
|
if (nextUrl == "end") return Res.error("No more data");
|
||||||
var res = nextUrl == null
|
var res = nextUrl == null
|
||||||
? await Network().getBookmarkedNovels(appdata.account!.user.id)
|
? await Network().getBookmarkedNovels(appdata.account!.user.id, public)
|
||||||
: await Network().getNovelsWithNextUrl(nextUrl!);
|
: await Network().getNovelsWithNextUrl(nextUrl!);
|
||||||
nextUrl = res.subData ?? "end";
|
nextUrl = res.subData ?? "end";
|
||||||
return res;
|
return res;
|
||||||
|
@@ -121,18 +121,18 @@ class _NovelPageState extends State<NovelPage> {
|
|||||||
padding: const EdgeInsets.only(bottom: 10),
|
padding: const EdgeInsets.only(bottom: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(width: 2),
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 68,
|
height: 68,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: ColorScheme.of(context).outlineVariant,
|
color: ColorScheme.of(context).outlineVariant,
|
||||||
width: 0.6),
|
width: 0.6,
|
||||||
borderRadius: BorderRadius.circular(4)),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
@@ -148,31 +148,31 @@ class _NovelPageState extends State<NovelPage> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(width: 8),
|
||||||
width: 12,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
widget.novel.totalViews.toString(),
|
widget.novel.totalViews.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: ColorScheme.of(context).primary,
|
color: ColorScheme.of(context).primary,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 18),
|
fontSize: 18,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(width: 16),
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 68,
|
height: 68,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: ColorScheme.of(context).outlineVariant, width: 0.6),
|
color: ColorScheme.of(context).outlineVariant,
|
||||||
borderRadius: BorderRadius.circular(4)),
|
width: 0.6,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
),
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
@@ -188,22 +188,19 @@ class _NovelPageState extends State<NovelPage> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(width: 8),
|
||||||
width: 12,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
widget.novel.totalBookmarks.toString(),
|
widget.novel.totalBookmarks.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: ColorScheme.of(context).primary,
|
color: ColorScheme.of(context).primary,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 18),
|
fontSize: 18,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
const SizedBox(width: 2),
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -241,25 +238,30 @@ class _NovelPageState extends State<NovelPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Column(
|
Expanded(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Text(widget.novel.author.name,
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.novel.author.name,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
)),
|
),
|
||||||
Text(
|
maxLines: 1,
|
||||||
widget.novel.createDate.toString().substring(0, 10),
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: ColorScheme.of(context).outline,
|
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
],
|
widget.novel.createDate.toString().substring(0, 10),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: ColorScheme.of(context).outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
const Icon(MdIcons.chevron_right)
|
const Icon(MdIcons.chevron_right)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -271,15 +273,43 @@ class _NovelPageState extends State<NovelPage> {
|
|||||||
|
|
||||||
bool isAddingFavorite = false;
|
bool isAddingFavorite = false;
|
||||||
|
|
||||||
|
var favoriteFlyout = FlyoutController();
|
||||||
|
|
||||||
Widget buildActions() {
|
Widget buildActions() {
|
||||||
void favorite() async {
|
void favorite() async {
|
||||||
if (isAddingFavorite) return;
|
if (isAddingFavorite) return;
|
||||||
|
bool? public;
|
||||||
|
if (!widget.novel.isBookmarked) {
|
||||||
|
await favoriteFlyout.showFlyout(
|
||||||
|
builder: (context) {
|
||||||
|
return MenuFlyout(
|
||||||
|
items: [
|
||||||
|
MenuFlyoutItem(
|
||||||
|
text: Text("Public".tl),
|
||||||
|
onPressed: () {
|
||||||
|
public = true;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuFlyoutItem(
|
||||||
|
text: Text("Private".tl),
|
||||||
|
onPressed: () {
|
||||||
|
public = false;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (public == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
isAddingFavorite = true;
|
isAddingFavorite = true;
|
||||||
});
|
});
|
||||||
var res = widget.novel.isBookmarked
|
var res = widget.novel.isBookmarked
|
||||||
? await Network().deleteFavoriteNovel(widget.novel.id.toString())
|
? await Network().deleteFavoriteNovel(widget.novel.id.toString())
|
||||||
: await Network().favoriteNovel(widget.novel.id.toString());
|
: await Network().favoriteNovel(widget.novel.id.toString(), public!);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
context.showToast(message: res.errorMessage ?? "Network Error");
|
context.showToast(message: res.errorMessage ?? "Network Error");
|
||||||
@@ -337,38 +367,41 @@ class _NovelPageState extends State<NovelPage> {
|
|||||||
context.to(() => NovelReadingPage(widget.novel));
|
context.to(() => NovelReadingPage(widget.novel));
|
||||||
}),
|
}),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Button(
|
FlyoutTarget(
|
||||||
onPressed: favorite,
|
controller: favoriteFlyout,
|
||||||
child: Row(
|
child: Button(
|
||||||
mainAxisAlignment: constrains.maxWidth > 420
|
onPressed: favorite,
|
||||||
? MainAxisAlignment.start
|
child: Row(
|
||||||
: MainAxisAlignment.center,
|
mainAxisAlignment: constrains.maxWidth > 420
|
||||||
children: [
|
? MainAxisAlignment.start
|
||||||
if (isAddingFavorite)
|
: MainAxisAlignment.center,
|
||||||
const SizedBox(
|
children: [
|
||||||
width: 18,
|
if (isAddingFavorite)
|
||||||
height: 18,
|
const SizedBox(
|
||||||
child: ProgressRing(
|
width: 18,
|
||||||
strokeWidth: 2,
|
height: 18,
|
||||||
),
|
child: ProgressRing(
|
||||||
)
|
strokeWidth: 2,
|
||||||
else if (widget.novel.isBookmarked)
|
),
|
||||||
Icon(
|
)
|
||||||
MdIcons.favorite,
|
else if (widget.novel.isBookmarked)
|
||||||
size: 18,
|
Icon(
|
||||||
color: ColorScheme.of(context).error,
|
MdIcons.favorite,
|
||||||
)
|
size: 18,
|
||||||
else
|
color: ColorScheme.of(context).error,
|
||||||
const Icon(MdIcons.favorite_outline, size: 18),
|
)
|
||||||
if (constrains.maxWidth > 420)
|
else
|
||||||
const SizedBox(width: 12),
|
const Icon(MdIcons.favorite_outline, size: 18),
|
||||||
if (constrains.maxWidth > 420) Text("Favorite".tl)
|
if (constrains.maxWidth > 420)
|
||||||
],
|
const SizedBox(width: 12),
|
||||||
)
|
if (constrains.maxWidth > 420) Text("Favorite".tl)
|
||||||
.fixWidth(shouldFillSpace
|
],
|
||||||
? width / 4 - 4 - kFluentButtonPadding
|
)
|
||||||
: 64)
|
.fixWidth(shouldFillSpace
|
||||||
.fixHeight(32),
|
? width / 4 - 4 - kFluentButtonPadding
|
||||||
|
: 64)
|
||||||
|
.fixHeight(32),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Button(
|
Button(
|
||||||
|
Reference in New Issue
Block a user